<%
'######################################################################
'## ab.db.asp
'## -------------------------------------------------------------------
'## Feature     :   AspBox Database Control
'## Version     :   v1.0.0.1
'## Author      :   Lajox(lajox@19www.com)
'## Update Date :   2014/07/30 20:36
'## Description :   AspBox database controller
'######################################################################

Class Cls_AB_DB

	Private s_connstr, s_dbType, s_pageParam, s_pageSpName
	Private i_queryType, i_errNumber, i_pageIndex, i_pageSize, i_pageCount, i_recordCount, i_fetchCount, i_affected
	Private o_conn, p_conn, o_rs, b_connect, b_debug, o_pageDic, a_setdb
	Private s_tbPrefix, s_tbSuffix '数据表前缀和后缀
	Public sql, acc, mysql, oracle

	Private Sub Class_Initialize()
		AB.Error(8)  = "查询记录出错"
		AB.Error(9)  = "获取记录失败"
		AB.Error(10) = "生成随机数出错！"
		AB.Error(11) = "无效的查询条件，无法获取记录集！"
		AB.Error(12) = "数据库服务器端连接错误，请检查数据库连接信息是否正确！"
		AB.Error(13) = "无效的数据库连接！"
		AB.Error(14) = "无效的查询条件，无法获取新的ID号！"
		AB.Error(15) = "生成Json格式代码出错！"
		AB.Error(16) = "生成不重复的随机字符串出错！"
		AB.Error(17) = "生成不重复的随机数出错！"
		AB.Error(18) = "获取随机记录失败，请输入要取的记录数量！"
		AB.Error(19) = "获取随机记录失败，请在表名后输入:ID字段的名称！"
		AB.Error(20) = "向数据库添加记录出错！"
		AB.Error(21) = "更新数据库记录出错！"
		AB.Error(22) = "从数据库删除数据出错！"
		AB.Error(23) = "从数据库获取数据出错！"
		AB.Error(32) = "仅支持从MS SQL Server数据库调用存储过程！"
		AB.Error(24) = "调用存储过程出错！"
		AB.Error(25) = "执行SQL语句出错！"
		AB.Error(26) = "生成SQL语句出错！"
		AB.Error(27) = "获取分页数据出错，数组必须是4个元素（必须提供数据库表的主键）！"
		AB.Error(28) = "获取分页数据出错，使用数组条件获取分页数据时条件参数必须为数组！"
		AB.Error(29) = "获取分页数据出错，使用自带分页存储过程时条件数组参数必须为6个元素！"
		AB.Error(30) = "获取分页数据出错，使用自定义分页存储过程时必须包含@@RecordCount和@@PageCount输出参数！"
		AB.Error(31) = "获取分页数据出错，使用存储过程获取分页数据时条件参数必须为数组！"
		b_debug 		= AB.Debug
		b_connect 		= False
		s_pageParam 	= "page"
		i_pageSize 		= 20
		s_pageSpName 	= "abx_sp_pager"
		Set o_pageDic 	= Server.CreateObject(AB.dictName)
		o_pageDic("default_html") = "<div class=""pager"">{first}{prev}{liststart}{list}{listend}{next}{last} 跳转到{jump}页</div>"
		o_pageDic("default_config") = ""
		Init()
	End Sub

	Private Sub Init()
		Err.Clear
		i_fetchCount 	= 0
		i_queryType 	= 0
		s_dbType 		= ""
		s_tbPrefix 		= AB.tbPrefix
		s_tbSuffix 		= AB.tbSuffix
		Set sql 		= New Cls_AB_DB_MSSQL
		Set acc 		= New Cls_AB_DB_ACCESS
		Set mysql 		= New Cls_AB_DB_MYSQL
		Set oracle 		= New Cls_AB_DB_ORACLE
	End Sub

	Private Sub Class_Terminate()
		If IsObject(o_conn) And TypeName(o_conn) = "Connection" Then
			If o_conn.State = 1 Then o_conn.Close()
			b_connect = False
		End If
		Set o_conn = Nothing
		Set o_pageDic 	= Nothing
		Set sql 		= Nothing
		Set acc 		= Nothing
		Set mysql 		= Nothing
		Set oracle 		= Nothing
	End Sub

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.New
	'@ 语  法:  Set db = AB.db.New
	'@ 返  回:  Object (ASP对象) 返回一个新的数据库操作类（Cls_AB_DB）对象
	'@ 作  用:  实例化一个新的AspBox数据库操作类对象
	'@ 			调用此方法将返回一个新的数据库操作类对象，用于在同一个程序页面上连接多个数据库。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ db Variable (变量) 已定义未初始化的变量
	'==DEMO=====================================================================================
	'@ 下面的例子说明了如何在同一页面上连接两个不同的数据库并可同时操作：
	'@ '连接至MSSQL数据库1
	'@ AB.db.Conn = AB.db.OpenConn(0, "Data1", "sa:123456@localhost")
	'@ Dim db, Ars, Brs
	'@ Set db = AB.db.New  '实例化新的对象
	'@ '连接至另一个Access数据库
	'@ db.Conn = AB.db.OpenConn(1, "/data/mydata.mdb", "")
	'@
	'@ '建立MSSQL数据库上的记录集：
	'@ Set Ars = AB.db.GetRecord("Table", "", "")
	'@ '......
	'@ '建立Access数据库上的记录集：
	'@ Set Brs = db.GetRecord("Table", "", "")
	'@ *****************************************************************************************

	Public Function [New]()
		Set [New] = New Cls_AB_DB
	End Function

	Public Property Let Debug(ByVal b)
		On Error Resume Next
		b_debug = b
		IF IsObject(AB.Error) Then
			AB.Error.debug = b
			IF Err.Number<>0 Then Err.Clear
		End IF
		On Error Goto 0
	End Property
	Public Property Get Debug
		Debug = b_debug
	End Property

	'@ *****************************************************************************************
	'@ 属性值:  AB.db.tbPrefix [= s]  可读/写
	'@ 返  回:  --
	'@ 作  用:  设置/获取 数据库表名前缀
	'@ 			若sql语句中有{prefix}关键字，则替换sql语句中的{prefix}关键字
	'==DESC=====================================================================================
	'@ 参数 s(可选): String (字符串)
	'==DEMO=====================================================================================
	'@ AB.db.tbPrefix = "tb_"
	'@ 'Set rs = AB.db.GRS("Select * From {prefix}user") '等同于 Set rs = AB.db.GRS("Select * From tb_user")
	'@ Set rs = AB.db.GRD("user", "id = 99") '等同于Set rs = AB.db.GRD("tb_user", "id = 99")
	'@ *****************************************************************************************

	Public Property Let tbPrefix(ByVal s)
		s_tbPrefix = s
	End Property
	Public Property Get tbPrefix()
		tbPrefix = s_tbPrefix
	End Property

	'@ *****************************************************************************************
	'@ 属性值:  AB.db.tbSuffix [= s]  可读/写
	'@ 返  回:  --
	'@ 作  用:  设置/获取 数据库表名后缀
	'==DESC=====================================================================================
	'@ 参数 s(可选): String (字符串)
	'==DEMO=====================================================================================
	'@ none
	'@ *****************************************************************************************

	Public Property Let tbSuffix(ByVal s)
		s_tbSuffix = s
	End Property
	Public Property Get tbSuffix()
		tbSuffix = s_tbSuffix
	End Property

	'@ *****************************************************************************************
	'@ 属  性:  AB.db.Conn 属性
	'@ 语  法:  AB.db.Conn [= connection]
	'@ 返  回:  Connection 对象(数据库连接对象) 数据库连接对象
	'@ 作  用:  设置和获取当前数据库连接对象，此属性可读可写
	'@ 			通过此属性可以设置或获取AB.db类的数据库连接对象。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ connection Connection (数据库连接对象)
	'@ 已经通过AB.db.OpenConn、AB.db.CreatConn或者其它方式建立并打开的数据库连接对象
	'==DEMO=====================================================================================
	'@ ---下面的例子可以设定AB.db类的数据库连接对象(AspBox推荐的方式)：
	'@ AB.db.Conn = AB.db.OpenConn(0,"TestData","sa:sapass@localhost")
	'@
	'@ ---如果你的页面上已经存在一个打开的Conn对象，也可以直接引用：
	'@ 'Conn连接对象已经通过其它方式建立并打开
	'@ AB.db.Conn = Conn
	'@ '外部建立的数据库连接对象也可以查询到数据库类型
	'@ AB.C.Print "已经连接的数据库类型是：" & AB.db.DatabaseType
	'@ *****************************************************************************************

	Public Property Let Conn(ByVal pdbConn)
		If TypeName(pdbConn) = "Connection" Then
			Set o_conn = pdbConn
			s_dbType = GetDataType(pdbConn)
		Else
			If b_debug Then
				AB.Error.Raise 13
			End If
		End If
	End Property

	Public Property Get Conn()
		If IsConn(o_conn) Then
			Set Conn = o_conn
		Else
			If IsConn(p_conn) Then Set Conn = p_conn
		End If
		If Not IsConn(o_conn) Then : Set Conn = Nothing : Exit Property : End If
	End Property

	'@ *****************************************************************************************
	'@ 属性值:  AB.db.isConnect  只读
	'@ 属性值:  AB.db.ifConn  只读
	'@ 返  回:  Boolean (布尔值)
	'@ 作  用:  检测 数据库是否连接上
	'@ *****************************************************************************************

	'@ *****************************************************************************************
	'@ 属性值:  AB.db.State  只读
	'@ 返  回:  1 或 0
	'@ 作  用:  获取 数据库连接状态, 数据库已连上返回值：1, 未连上返回值：0
	'@ *****************************************************************************************

	Public Property Get isConnect:isConnect = b_connect:End Property
	Public Property Get State
		If b_connect Then : State = 1 : Else : State = 0 : End If
	End Property

	Public Property Get ifConn()
		Dim b_cn : b_cn = False
		If TypeName(o_conn) = "Connection" Then
			If o_conn.State = 1 Then b_cn = True Else b_cn = False
		Else
			b_cn = False
		End If
		ifConn = b_cn
	End Property

	'@ *****************************************************************************************
	'@ 属  性:  AB.db.DatabaseType 属性
	'@ 别  名:  AB.db.DbType
	'@ 语  法:  AB.db.DatabaseType
	'@ 返  回:  String (字符串) 返回表述数据库类型的字符串，例如：ACCESS、MSSQL、MYSQL或ORACLE
	'@ 作  用:  查询当前使用的数据库类型，此属性只读
	'@ 			可通过此属性查询当前使用的数据库类型。
	'==DESC=====================================================================================
	'@ 参数说明：无参数
	'@ *****************************************************************************************

	Public Property Get DatabaseType()
		s_dbType = UCase(s_dbType)
		Select Case UCase(s_dbType)
			Case "0","MSSQL","SQL" : s_dbType = "MSSQL"
			Case "1","ACCESS","ACC","" : s_dbType = "ACCESS"
			Case "2","MYSQL" : s_dbType = "MYSQL"
			Case "3","ORACLE" : s_dbType = "ORACLE"
			Case Else : s_dbType = "ACCESS"
		End Select
		DatabaseType = s_dbType
	End Property

	Public Property Let DatabaseType(byval strType)
		Select Case UCase(strType)
			Case "0","MSSQL","SQL" : s_dbType = "MSSQL"
			Case "1","ACCESS","ACC","" : s_dbType = "ACCESS"
			Case "2","MYSQL" : s_dbType = "MYSQL"
			Case "3","ORACLE" : s_dbType = "ORACLE"
			Case Else : s_dbType = "ACCESS"
		End Select
	End Property

	'@ *****************************************************************************************
	'@ 属性值:  AB.db.DbType [= s]  可读/写
	'@ 返  回:  --
	'@ 作  用:  设置/获取 数据库类型
	'==DESC=====================================================================================
	'@ 参数 s(可选): String (字符串) acc/sql
	'==DEMO=====================================================================================
	'@ AB.db.DbType = "ACCESS"
	'@ *****************************************************************************************

	Public Property Get DbType()
		DbType = DatabaseType()
	End Property

	Public Property Let DbType(byval strType)
		Select Case UCase(strType)
			Case "0","MSSQL","SQL" : s_dbType = "MSSQL"
			Case "1","ACCESS","ACC","" : s_dbType = "ACCESS"
			Case "2","MYSQL" : s_dbType = "MYSQL"
			Case "3","ORACLE" : s_dbType = "ORACLE"
			Case Else : s_dbType = "ACCESS"
		End Select
	End Property

	'@ *****************************************************************************************
	'@ 属  性:  AB.db.QueryType 属性
	'@ 语  法:  AB.db.QueryType = type
	'@ 返  回:  无返回值
	'@ 作  用:  设置用ADO获取记录集的方式，此属性为只写
	'@ 			设置用ADO获取记录集时是用RecordSet还是Command方式。
	'@ 			type参数为1或者"command"时用ADODB.Command的方式获取记录集，
	'@ 			为0 或者"recordset"时用ADODB.RecordSet方式。默认为使用ADODB.RecordSet方式。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ type Integer (整数) 或 String (字符串)
	'@ 	为值: 0 | rs | recordset | rst1 | rst 	ADO用ADODB.RecordSet方式获取记录集，默认为0
	'@ 	为值: 1 | cmd | command 				ADO用ADODB.Command方式获取记录集
	'@ 	为值: 2 | rst2 							则类似 Rs.Open sql,Conn,3,3 形式获取记录集
	'@ 	为值: 3 | rst3 							则类似 Rs.Open sql,Conn,1,3 形式获取记录集
	'@ 	为值: 4 | exec | exe 					则类似 Conn.Execute(sql) 形式获取记录集
	'@ *****************************************************************************************

	Public Property Let QueryType(ByVal str)
		Select Case LCase(str)
			Case "1","command","cmd" : i_queryType = 1
			Case "2","rst2" : i_queryType = 2
			Case "3","rst3" : i_queryType = 3
			Case "0","recordset","rs","rst","rst1" : i_queryType = 0
			Case Else : i_queryType = 0
		End Select
	End Property

	Public Property Get QueryType()
		Select Case LCase(i_queryType)
			Case "1","command","cmd" : i_queryType = 1
			Case "2","rst2" : i_queryType = 2
			Case "3","rst3" : i_queryType = 3
			Case "0","recordset","rs","rst1" : i_queryType = 0
			Case Else : i_queryType = 0
		End Select
		QueryType = i_queryType
	End Property

	'@ *****************************************************************************************
	'@ 属  性:  AB.db.PageSize 属性
	'@ 语  法:  AB.db.PageSize[ = number]
	'@ 返  回:  Integer (整数) 返回当前使用分页的记录集的每页记录数
	'@ 作  用:  设置和查询AspBox分页时的每一页的记录数，此属性可读可写
	'@ 			此属性将设置AspBox分页时每一页的记录数，
	'@ 			如果不设置此属性，则默认为20条。此属性将设置你的程序的所有分页数据都是每页number条，
	'@ 			如果要临时按某个记录数分页，则可以在用 AB.db.GetPageRecord 方法获取分页数据时临时设置。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ number(可选) Integer (整数) 指定每页的记录数
	'@ *****************************************************************************************

	Public Property Let PageSize(ByVal num)
		i_pageSize = num
	End Property

	Public Property Get PageSize()
		PageSize = i_pageSize
	End Property

	'@ *****************************************************************************************
	'@ 属  性:  AB.db.PageCount 属性
	'@ 语  法:  AB.db.PageCount
	'@ 返  回:  Integer (整数) 返回当前使用分页记录集的总页数
	'@ 作  用:  查询分页记录集总页数，此属性为只读
	'@ 			可通过输出此属性的值来查询分页记录集的总页数。
	'==DESC=====================================================================================
	'@ 参数说明：无参数
	'@ *****************************************************************************************

	Public Property Get PageCount()
		PageCount = i_pageCount
	End Property

	'@ *****************************************************************************************
	'@ 属  性:  AB.db.PageIndex 属性
	'@ 语  法:  AB.db.PageIndex
	'@ 返  回:  Integer (整数) 返回当前使用分页记录集的所在页码
	'@ 作  用:  查询分页记录集当前页码，此属性为只读
	'@ 			可通过输出此属性的值来查询分页记录集的当前页码
	'==DESC=====================================================================================
	'@ 参数说明：无参数
	'@ *****************************************************************************************

	Public Property Get PageIndex()
		PageIndex = AB.C.IIF(AB.C.isNul(i_pageIndex),GetCurrentPage,i_pageIndex)
	End Property

	'@ *****************************************************************************************
	'@ 属  性:  AB.db.PageRecordCount 属性
	'@ 语  法:  AB.db.PageRecordCount
	'@ 返  回:  Integer (整数) 返回当前使用分页的记录集的总记录数
	'@ 作  用:  查询分页记录集总记录数，此属性为只读
	'@ 			可通过输出此属性的值来查询分页记录集的总记录数。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ number(可选) Integer (整数) 指定每页的记录数
	'@ *****************************************************************************************

	Public Property Get PageRecordCount()
		PageRecordCount = i_recordCount
	End Property

	'@ *****************************************************************************************
	'@ 属  性:  AB.db.PageParam 属性
	'@ 语  法:  AB.db.PageParam = string
	'@ 返  回:  无返回值
	'@ 作  用:  设置分页时的指定页码的URL参数名称，此属性只写
	'@ 			此属性将设置AspBox分页时获取当前页码的Url参数名称，如果不设置此属性，则默认为"page"。
	'@ 			此属性通常只用在你的程序的所有页面都没有用page作为默认页码Url参数的情况下，
	'@ 			如果只是临时某个页面没有用page作为页码Url参数或者在一个页面中有多个分页的情况下，
	'@ 			则可以在用 AB.db.GetPageRecord 方法获取分页数据时临时设置。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ string String (字符串) 指定页码的Url参数名称
	'==DEMO=====================================================================================
	'@ 下面的例子说明了此属性的作用：
	'@ 例如页面如果为：
	'@  http://www.ambox.cn/data/list.asp?page=3
	'@ 此页面指定页码的Url参数为page，所以不用设置此属性。
	'@ 但如果页面为：
	'@  http://www.ambox.cn/data/list.asp?p=3
	'@ 则可以事先设置此属性为：
	'@  AB.db.PageParam = "p"
	'@ *****************************************************************************************

	Public Property Let PageParam(ByVal str)
		s_pageParam = str
	End Property

	'@ *****************************************************************************************
	'@ 属  性:  AB.db.PageSpName 属性
	'@ 语  法:  AB.db.PageSpName = spName
	'@ 返  回:  无返回值
	'@ 作  用:  设置默认分页存储过程名称，此属性只写
	'@ 			此属性将设置AspBox使用MSSQL存储过程分页时调用的存储过程名称，
	'@ 			如果不设置此属性，则默认为"abx_sp_pager"。
	'@ 			如果要在分页时使用其它名称的存储过程，也可以在用 AB.db.GetPageRecord 方法获取分页数据时临时设置。
	'@ *****************************************************************************************

	Public Property Let PageSpName(ByVal str)
		s_pageSpName = str
	End Property

	'@ *****************************************************************************************
	'@ 属  性:  AB.db.ConnStr
	'@ 返  回:  String (字符串) 获取数据库连接字符串
	'@ 作  用:  获取数据库连接字符串
	'==DESC=====================================================================================
	'@ 参数: 无
	'==DEMO=====================================================================================
	'@ AB.C.Print AB.db.ConnStr '输出取得的数据库连接字符串
	'@ *****************************************************************************************

	Public Property Get ConnStr()
		s_connstr = getConnStr__()
		ConnStr = s_connstr
	End Property

	'@ *****************************************************************************************
	'@ 属  性:  AB.db.DbSet
	'@ 返  回:  Array (数组) (最近一次)数据库设置信息数组
	'@ 作  用:  获取(最近一次)数据库设置信息(数组)
	'==DESC=====================================================================================
	'@ 参数: 无
	'==DEMO=====================================================================================
	'@ AB.Trace AB.db.dbSet '获取(最近一次)数据库设置信息
	'@ *****************************************************************************************

	Public Property Get DBSet()
		Dim temp : If IsArray(a_setdb) Then temp = a_setdb
		DBSet = temp
	End Property

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.Set
	'@ 返  回:  Array (数组) 该数组包含数据库配置信息
	'@ 作  用:  设置数据库信息
	'==DESC=====================================================================================
	'@ 参数: 无
	'==DEMO=====================================================================================
	'@ 下面的例子将对比建立几种不同的数据库连接对象：
	'@ '连接至MS SQL Server，服务器地址为192.168.0.2，用户名为sa，密码为pass，数据库为TestData
	'@ 'Call AB.db.Set(0,"TestData","sa:pass@192.168.0.2")
	'@ '连接至MS Access数据库，数据库文件为相对路径，密码为accpass
	'@ 'Call AB.db.Set(1,"/data/Test.mdb","accpass")
	'@ '连接至MS Access数据库，数据库文件为绝对路径，没有密码
	'@ 'Call AB.db.Set("access","D:\Web\data\Test.mdb","")
	'@ '连接至MySQL Server，服务器地址为192.168.0.2，端口为3307，用户名为root，密码为pass，数据库为TestData
	'@ 'Call AB.db.Set("mysql","TestData","root:pass@192.168.0.2:3307")
	'@ '当然，也可以定义其他连接对象
	'@ 'Call AB.db.Set("MSSQL","myData","sa:pass@localhost:7878")
	'@ '------------------------------------------------------------------
	'@ Dim temp : temp = AB.db.Set(1,"/data/Test.mdb","") '返回值是数组
	'@ Dim dbConn
	'@ 'Set dbConn = AB.db.CreatConn(AB.db.ConnStr)
	'@ Set dbConn = AB.db.Open()
	'@ AB.db.Conn = dbConn
	'@ AB.Trace dbConn
	'@ *****************************************************************************************

	Public Function [Set](ByVal dbType, ByVal strDB, ByVal strServer)
		Dim TempStr, objConn, s, u, p, port, [Return]
		s = "" : u = "" : p = "" : port = ""
		If Instr(strServer,"@")>0 Then
			s = Trim(Mid(strServer,InstrRev(strServer,"@")+1))
			u = Trim(Left(strServer,InstrRev(strServer,"@")-1))
			If Instr(s,":")>0 Then : port = Trim(Mid(s,Instr(s,":")+1)) : s = Trim(Left(s,Instr(s,":")-1))
			If Instr(u,":")>0 Then : p = Trim(Mid(u,Instr(u,":")+1)) : u = Trim(Left(u,Instr(u,":")-1))
		Else
			If Instr(strServer,":")>0 Then
				u = Trim(Left(strServer,Instr(strServer,":")-1))
				p = Trim(Mid(strServer,Instr(strServer,":")+1))
			Else
				p = Trim(strServer)
			End If
		End If
		s_dbType = UCase(Cstr(dbType))
		If s_dbType = "" Then s_dbType = "ACCESS"
		Select Case UCase(dbType)
			Case "0","MSSQL","SQL"
				sql.server = s : sql.db = strDB : sql.uid = u : sql.pwd = p
				If port <> "" Then sql.port = port
				s_dbType = "MSSQL"
				[Return] = Array(s_dbType, sql.server, sql.db, sql.uid, sql.pwd, sql.port)
			Case "1","ACCESS","ACC",""
				acc.db = strDB : acc.pwd = p
				s_dbType = "ACCESS"
				[Return] = Array(s_dbType, acc.db, acc.pwd)
			Case "2","MYSQL"
				mysql.server = s : mysql.db = strDB : mysql.uid = u : mysql.pwd = p
				If port = "" Then port = "3306" : mysql.port = port
				s_dbType = "MYSQL"
				[Return] = Array(s_dbType, mysql.server, mysql.db, mysql.uid, mysql.pwd, mysql.port)
			Case "3","ORACLE"
				oracle.server = s : oracle.uid = u : oracle.pwd = p
				s_dbType = "ORACLE"
				[Return] = Array(s_dbType, oracle.server, oracle.uid, oracle.pwd)
		End Select
		a_setdb = [Return]
		[Set] = [Return] '返回数组(该数组包含数据库配置信息)
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.OpenConn
	'@ 语  法:  AB.db.OpenConn type, name, accessPass|user[:password]@server[:port]
	'@ 返  回:  Connection (数据库连接对象) 返回一个已经连接并打开的数据库连接对象(Adodb.Connection)
	'@ 作  用:  调用此方法可以建立一个数据库连接对象(Adodb.Connection)。此对象可用 AB.db.C 方法关闭并释放。
	'@ 			此方法仅内置了4种数据库的连接方式，如要连接其他类型数据库可用 AB.db.CreatConn 方法用自定义字符串建立连接对象。
	'@ 			查询各种数据库连接字符串，请访问http://www.connectionstrings.com。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ type Integer (整数) 或 String (字符串)
	'@ 数据库类型，可以是以下值：
	'@   0 或 "MSSQL" - 连接至MS SQL Server
	'@   1 或 "ACCESS" - 连接至MS Access 数据库
	'@   2 或 "MYSQL" - 连接至MySQL数据库
	'@   3 或 "ORACLE" - 连接至Oracle数据库
	'@ name String (字符串)
	'@ 数据库名称。对于Access数据库来说就是包含路径的文件名，可以是相对或绝对路径。
	'@ accessPass String (字符串)
	'@ Access数据库的密码，没有密码此参数请留空
	'==DEMO=====================================================================================
	'@ 下面的例子将对比建立几种不同的数据库连接对象：
	'@ '连接至MS SQL Server，服务器地址为192.168.0.2，用户名为sa，密码为pass，数据库为TestData
	'@ AB.db.Conn = AB.db.OpenConn(0,"TestData","sa:pass@192.168.0.2")
	'@ '连接至MS Access数据库，数据库文件为相对路径，密码为accpass
	'@ AB.db.Conn = AB.db.OpenConn(1,"/data/Test.mdb","accpass")
	'@ '连接至MS Access数据库，数据库文件为绝对路径，没有密码
	'@ AB.db.Conn = AB.db.OpenConn("access","D:\Web\data\Test.mdb","")
	'@ '连接至MySQL Server，服务器地址为192.168.0.2，端口为3307，用户名为root，密码为pass，数据库为TestData
	'@ AB.db.Conn = AB.db.OpenConn("mysql","TestData","root:pass@192.168.0.2:3307")
	'@ '当然，也可以定义其他连接对象
	'@ Dim Conn : Set Conn =  AB.db.OpenConn("MSSQL","myData","sa:pass@localhost:7878")
	'@ -----------------------------------------------------------------------------------
	'@ 初始化连接可以这么做：
	'@ If LCase(AB.DT("dbtype"))="sql" Or LCase(AB.DT("dbtype"))="mssql" Then '@MS SQL Server
	'@ 	If AB.HasCore("DB") Then AB.db.Conn = AB.db.OpenConn(0,AB.DT("sqldb"),""&AB.DT("sqluid")&":"&AB.DT("sqlpwd")&"@"&AB.DT("sqlserver")&"")
	'@ Else '@Access
	'@ 	If AB.HasCore("DB") Then AB.db.Conn = AB.db.OpenConn(1,AB.DT("accdb"),AB.DT("accpwd"))
	'@ End If
	'@ *****************************************************************************************

	Public Function OpenConn(ByVal dbType, ByVal strDB, ByVal strServer)
		Dim temp : temp = [Set](dbType, strDB, strServer)
		s_connstr = Me.ConnStr
		Set OpenConn = CreatConn(s_connstr)
		If IsConn(OpenConn) Then : Set p_conn = OpenConn : Conn = OpenConn : End If
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.CreatConn
	'@ 语  法:  AB.db.CreatConn connstr
	'@ 返  回:  Connection (数据库连接对象) 已经建立的数据库连接对象
	'@ 作  用:  根据自定义连接字符串建立数据库连接对象
	'@ 			此方法用于根据指定的连接字符串建立数据库连接对象。
	'@ 			查询各种数据库连接字符串，请访问http://www.connectionstrings.com。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ connstr (String) 数据库连接字符串
	'==DEMO=====================================================================================
	'@ 比如下面的例子可以生成一个连接至PostgreSQL数据库的字符串：
	'@ Dim Conn
	'@ Set Conn = AB.db.CreatConn("Provider=PostgreSQL OLE DB Provider;Data Source=myServerAddress;location=myDataBase;User ID=myUsername;password=myPassword;timeout=1000;")
	'@ AB.db.Conn = Conn
	'@ *****************************************************************************************

	Public Function CreatConn(ByVal ConnStr)
		On Error Resume Next
		Dim objConn : Set objConn = Server.CreateObject("ADODB.Connection")
		objConn.Open ConnStr
		If Err.Number = 0 Then b_connect = True
		If Err.number <> 0 Then
			b_connect = False
			'objConn.Close
			Set objConn = Nothing
			If b_debug Then
				AB.Error.Msg = "<br />(""" & ConnStr & """)"
				AB.Error.Raise 12
			End If
		End If
		Set CreatConn = objConn
		If IsConn(CreatConn) Then : Set p_conn = CreatConn : If Not IsConn(Conn) Then Conn = CreatConn : End If
		On Error Goto 0
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.Open
	'@ 语  法:  Set Conn = AB.db.Open
	'@ 返  回:  Connection (数据库连接对象) 已经建立的数据库连接对象
	'@ 作  用:  根据自定义连接字符串建立数据库连接对象
	'==DESC=====================================================================================
	'@ 参数：无
	'==DEMO=====================================================================================
	'@ Dim newConn
	'@ 'Set newConn = AB.db.OpenConn("access","D:\Web\data\Test.mdb","")
	'@ Call AB.db.Set("access","D:\Web\data\Test.mdb","")
	'@ Set newConn = AB.db.Open()
	'@ AB.db.Conn = newConn
	'@ AB.Trace newConn
	'@ *****************************************************************************************

	Public Function Open()
		s_connstr = Me.ConnStr
		Set Open = CreatConn(s_connstr)
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.isConn
	'@ 语  法:  isConnObj = AB.db.isConn(obj)
	'@ 返  回:  Boolean
	'@ 作  用:  检测是否为 Connection 对象(数据库连接对象)
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ obj Anything (任意值) 检测该值是否为 Connection 对象
	'==DEMO=====================================================================================
	'@ AB.C.Print AB.db.isConn(AB.db.Conn)
	'@ *****************************************************************************************

	Public Function IsConn(ByVal obj)
		IsConn = False
		If Not IsObject(obj) Then Exit Function
		If Lcase(TypeName(obj)) = "connection" Then IsConn = True
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.GetDataType
	'@ 语  法:  AB.db.GetDataType connObj
	'@ 返  回:  数据库连接类型(MSSQL/ACCESS/MYSQL/ORACLE)
	'@ 作  用:  获取数据库连接对象的连接类型
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ connObj Object (对象) 数据库连接对象
	'==DEMO=====================================================================================
	'@ AB.C.Print AB.db.GetDataType(AB.db.Conn)
	'@ *****************************************************************************************

	Public Function GetDataType(ByVal connObj)
		If Not IsConn(connObj) Then Exit Function
		Dim str,i : str = UCase(connObj.Provider)
		Dim MSSQL, ACCESS, MYSQL, ORACLE
		MSSQL = Split("SQLNCLI10, SQLXMLOLEDB, SQLNCLI, SQLOLEDB, MSDASQL",", ")
		ACCESS = Split("MICROSOFT.ACE.OLEDB.12.0, MICROSOFT.JET.OLEDB.4.0",", ")
		MYSQL = "MYSQLPROV"
		ORACLE = Split("MSDAORA, OLEDB.ORACLE",", ")
		For i = 0 To Ubound(MSSQL)
			If Instr(str,MSSQL(i))>0 Then
				GetDataType = "MSSQL" : Exit Function
			End If
		Next
		For i = 0 To Ubound(ACCESS)
			If Instr(str,ACCESS(i))>0 Then
				GetDataType = "ACCESS" : Exit Function
			End If
		Next
		If Instr(str,MYSQL)>0 Then
			GetDataType = "MYSQL" : Exit Function
		End If
		For i = 0 To Ubound(ORACLE)
			If Instr(str,ORACLE(i))>0 Then
				GetDataType = "ORACLE" : Exit Function
			End If
		Next
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.GetTables(conn) 获取所有表名
	'@ 返  回:  Array
	'@ 作  用:  获取所有表名信息数组
	'==DESC=====================================================================================
	'@ 参数 conn :	Connection Object (数据库连接对象)
	'@ 				当 conn 为空时，返回本当前数据库连接的所有表数组信息
	'==DEMO=====================================================================================
	'@ AB.Trace AB.db.GetTables("") '输出： Array("Table1","Table2","Table3")
	'@ *****************************************************************************************

	Public Function GetTables(Byval conn)
		On Error Goto 0
		Dim tables, t, tb, arr, i : tables = Array()
		If AB.C.IsNul(conn) Or Not Me.isConn(conn) Then
			Set t = Me.Conn.OpenSchema(20,Array(Empty,Empty,Empty,"TABLE"))
		Else
			Set t = conn.OpenSchema(20,Array(Empty,Empty,Empty,"TABLE"))
		End If
		If IsObject(t) Then
			If Not (t.eof Or t.bof) Then arr = t.GetRows(-1)
			If Not AB.C.IsNul(arr) Then
				For i = 0 To Ubound(arr,2)
					tb = arr(2,i)
					tables = AB.A.Push(tables, tb)
				Next
			End If
			t.Close()
			Set t = Nothing
		End If
		GetTables = tables
		On Error Resume Next
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.tbExists
	'@ 语  法:  AB.db.tbExists(table[:field])
	'@ 返  回:  Boolean (True / False) 检测是否存在数据表 或 检测数据表中是否含有某字段
	'@ 作  用:  检测数据库是否存在某数据表 或 检测某数据表中是否存在某字段
	'@ 			当省略field参数时，用于检测数据库是否存在某数据表
	'@ 			当存在field参数时，用于检测某数据表中是否存在某字段
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ table  String (字符串) 数据表名称
	'@ field  String (字符串) 字段名称
	'==DEMO=====================================================================================
	'@ AB.C.Print AB.db.tbExists("table1") '检测数据表table1是否存在
	'@ AB.C.Print AB.db.tbExists("table1:field1") '检测数据表table1中是否含有field1字段
	'@ *****************************************************************************************

	Public Function tbExists(Byval TableName)
		Dim isExist : isExist = False
		Dim tb,fi : tb = TableName
		If InStr(tb,":") > 0 Then
			tb = Abx_Param(TableName)(0)
			fi = Abx_Param(TableName)(1)
			isExist = fieldExists(tb, fi)
		Else
			isExist = Abx_tbExists(tb)
		End If
		tbExists = isExist
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.fieldExists
	'@ 语  法:  AB.db.fieldExists(table, field)
	'@ 返  回:  Boolean (True / False) 检测数据表中是否存在某个字段
	'@ 作  用:  检测指定数据表中是否存在某个字段
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ table  String (字符串) 数据表名称
	'@ field  String (字符串) 字段名称
	'==DEMO=====================================================================================
	'@ AB.C.Print AB.db.fieldExists("table1","field1")
	'@ *****************************************************************************************

	Public Function fieldExists(Byval table, Byval field)
		On Error Resume Next
		Dim isExist : isExist = False
		Dim tb : tb = table : tb = "{prefix}" & DelFix(tb)
		tb = FixSQL(tb)
		Dim sql : sql = "select * from "& tb &""
		Dim item,tRs,temp : Set tRs = Exec(sql)
		For Each item In tRs.Fields
			temp = item.name
			If LCase(field) = LCase(temp) Then isExist = True
		Next
		Close(tRs)
		fieldExists = isExist
		If Err.Number <> 0 Then
			AB.Error.Msg = "<br />" & sql
			AB.Error.Raise 11
			Err.Clear
		End If
		On Error Goto 0
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.tbAutoID
	'@ 语  法:  AB.db.tbAutoID(TableName)
	'@ 返  回:  String (字符串) 获取数据表自动编号字段名
	'@ 作  用:  用于获取数据表自动编号字段名
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ TableName  String (字符串) 数据表名称
	'==DEMO=====================================================================================
	'@ AB.C.Print AB.db.tbAutoID("table1")
	'@ *****************************************************************************************

	Public Function tbAutoID(ByVal table)
		If TypeName(o_conn)<>"Connection" Then Exit Function
		Dim t,dbtype,t_autoid,t_field,t_isautoid,t_nullable,t_default
		Dim tb : tb = table : tb = "{prefix}" & DelFix(tb)
		tb = FixSQL(tb) : tb = Trim(AB.C.CLeft(tb,":"))
		Set t = o_conn.OpenSchema(4,Array(Empty,Empty,tb,Empty))
		dbtype = DatabaseType
		Do While Not t.Eof
			t_field = t("COLUMN_NAME")
			If dbtype = "MSSQL" Then
				t_isautoid = isSQLAutoID(t("DATA_TYPE"),t("COLUMN_FLAGS"))
			ElseIf dbtype = "ACCESS" Then
				t_isautoid = isACCAutoID(t("DATA_TYPE"),t("COLUMN_FLAGS"))
			End If
			t_nullable = AB.C.IIF(t("IS_NULLABLE"),True,False)
			t_default = t("COLUMN_DEFAULT")
			If t_isautoid Then : t_autoid = t_field : Exit Do : End If
			t.MoveNext
		Loop
		tbAutoID = t_autoid
		Close(t)
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.AutoId
	'@ 语  法:  AB.db.AutoId TableName[:FieldName]
	'@ 返  回:  Integer (整数) 新的自动编号
	'@ 作  用:  调用此方法将返回一个数据表的新的唯一序列号值，相当于数据库的自动编号。
	'@ 			如有可能请尽量提供ID字段的名称，这样将大大提高生成新编号的效率。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ TableName 		String (字符串) 数据表名称
	'@ FieldName(可选) 	String (字符串) ID字段名称,如留空则默认为数据表的第一个字段
	'==DEMO=====================================================================================
	'@ 下面的例子说明了如何获取数据表的新的唯一编号：
	'@ Dim newId
	'@ newId = AB.db.AutoId("Table:ID") '带ID字段名称（推荐）
	'@ 'newId = AB.db.AutoId("Table") '不带ID字段名称
	'@ AB.C.Print(newId)
	'@ *****************************************************************************************

	Public Function AutoID(ByVal TableName)
		On Error Resume Next
		Dim rs, newRs, tmp, fID, tmpID : fID = "" : tmpID = 0
		Dim sqlt1, sqlt2, tmp1, tmp2, rst1, rst2, p : p = 1
		Dim tb : tb = TableName : tb = DelFix(tb)
		tmp = Abx_Param(tb)
		tb = trim(tmp(0))
		If AB.C.Has(tmp(1)) Then : fID = trim(tmp(1)) : tmp = "" : End If
		If p = 1 Then '采用此算法（精确）
			tmp1 = tbAutoID(tb)
			tmp2 = AB.C.IIF(fID<>"", ""&fID&"", ""&tmp1&"")
			If tbExists("LXTEST_TB_TEMP1") Then DoExecute("Drop Table {prefix}LXTEST_TB_TEMP1")
			sqlt1 = FixSQL("select "&tmp2&" into {prefix}LXTEST_TB_TEMP1 from  [{prefix}"&tb&"]")
			DoExecute(sqlt1)
			sqlt2 = FixSQL("select * from {prefix}LXTEST_TB_TEMP1")
			Set rst1 = DoExecute(sqlt2)
			If Err.number <> 0 Then AB.Error.Raise 14
			rst1.addnew : rst1.update : Set rst1 = Nothing
			Set rs = GRS("Select " & AB.C.IIF(fID<>"", "Max("&fID&")", "Top 1 *") & " From {prefix}LXTEST_TB_TEMP1")
			If rs.eof Then
				AutoID = 1 : Exit Function
			Else
				If fID<>"" Then
					If AB.C.isNul(rs.Fields.Item(0).Value) Then AutoID = 0 : Exit Function
					AutoID = rs.Fields.Item(0).Value : Exit Function
				Else
					Set newRs = GRS("Select Max("&rs.Fields.Item(0).Name&") From [{prefix}LXTEST_TB_TEMP1]")
					tmpID = newRS.Fields.Item(0).Value
					newRs.Close() : Set newRs = Nothing
				End If
			End If
			If tbExists("LXTEST_TB_TEMP1") Then DoExecute("Drop Table {prefix}LXTEST_TB_TEMP1")
		Else
			Set rs = GRS("Select " & AB.C.IIF(fID<>"", "Max("&fID&")", "Top 1 *") & " From [{prefix}"&tb&"]")
			If rs.eof Then
				AutoID = 1 : Exit Function
			Else
				If fID<>"" Then
					If AB.C.isNul(rs.Fields.Item(0).Value) Then AutoID = 1 : Exit Function
					AutoID = rs.Fields.Item(0).Value + 1 : Exit Function
				Else
					Set newRs = GRS("Select Max("&rs.Fields.Item(0).Name&") From [{prefix}"&tb&"]")
					tmpID = newRS.Fields.Item(0).Value + 1
					newRs.Close() : Set newRs = Nothing
				End If
			End If
		End If
		Close(rs)
		If Err.number <> 0 Then AB.Error.Raise 14
		AutoID = tmpID
		On Error Goto 0
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.GetRecord
	'@ 语  法:  Set rs = AB.db.GetRecord(table[:fields][:topN], condition, orderBy)
	'@ 别  名:  Set rs = AB.db.GR(table[:fields][:topN], condition, orderBy)
	'@ 返  回:  Recordset (记录集对象) 符合查询条件的记录集对象
	'@ 作  用:  调用此方法可以返回一个符合查询条件的记录集对象(Adodb.Recordset)。此对象可用 AB.db.C 方法关闭并释放。
	'@ 			fields 和 topN 不是必须的， 但如果需要，可以直接在 table 参数中加冒号后跟上即可。 要查看此方法生成的SQL查询语句，可用 AB.db.WGetRecord 方法。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ TableName 		String (字符串) 数据表名称
	'@ FieldName(可选) 	String (字符串) ID字段名称,如留空则默认为数据表的第一个字段
	'==DEMO=====================================================================================
	'@ 下面的例子将建立一个记录集：
	'@ Dim rs
	'@ Set rs = AB.db.GetRecord("TestTable:fId,fName,fAge","fSex='男' And IsActive = 1","fName Asc")
	'@ '等同于： Set rs = AB.db.GetRecordBySQL("Select fId,fName,fAge From [TestTable] Where fSex='男' And IsActive = 1 Order By fName Asc")
	'@ '		 Set rs = AB.db.GRS("Select fId,fName,fAge From [TestTable] Where fSex='男' And IsActive = 1 Order By fName Asc")
	'@ '也可写成： Set rs = AB.db.Exec("Select id,name,artist From [LB_C_Media] Where id < 10 Order By id Asc")
	'@ While Not rs.eof
	'@   Response.Write ("Name is:" & rs(1) & " Age is:" & rs(2) & "<br />")
	'@   rs.movenext()
	'@ Wend
	'@ AB.db.C(rs)
	'@
	'@ 下面的例子将用数组参数的方法建立一个记录集，并且只取前20条记录：
	'@ Set rs = AB.db.GR("TestTable:20", Array("fSex:男","IsActive:1"), "fName Asc")
	'@ '等同于：Set rs = AB.db.GRS("Select Top 20 * From [TestTable] Where fSex='男' And IsActive = 1 Order By fName Asc")
	'@
	'@ 下面再举一个综合的例子：
	'@ Set rs = AB.db.GR("TestTable:fId,fName,Datediff(year,fBirth,getdate()) As nowAge:20", Array("fSex:男","IsActive:1"), "fName Asc")
	'@ '等同于：Set rs = AB.db.GRS("Select Top 20 fId,fName,Datediff(year,fBirth,getdate()) As nowAge From [TestTable] Where fSex='男' And IsActive = 1 Order By fName Asc")
	'@ *****************************************************************************************

	Public Function GetRecord(ByVal TableName,ByVal Condition,ByVal OrderField)
		Set GetRecord = GRS(wGetRecord(TableName,Condition,OrderField))
	End Function

	Public Function GR(ByVal TableName,ByVal Condition,ByVal OrderField)
		Set GR = GetRecord(TableName, Condition, OrderField)
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.wGetRecord
	'@ 语  法:  AB.db.wGetRecord(table[:fields][:topN], condition, orderBy)
	'@ 别  名:  AB.db.wGR(table[:fields][:topN], condition, orderBy)
	'@ 返  回:  String (字符串) SQL字符串
	'@ 作  用:  生成添加一个新的纪录的SQL语句
	'@ 			调用此方法将返回一个由指定条件生成的SQL查询语句。如果调用 AB.db.GetRecord 方法失败时，可用此方法输出相关的SQL语句进行排错。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ table String (字符串) 数据表名称
	'@ fields(可选) String (字符串) 字段名称，用逗号隔开，省略此参数则为全部字段
	'@ topN(可选) Integer (整数) 获取纪录的数量，为0或省略此参数则是全部记录
	'@ condition Array (数组) 或 String (字符串) 查询条件，不包含“Where”，如果是数组应遵循 "数组参数约定"
	'@ orderBy String (字符串) 排序方式，不包含“Order By”，如："Age Desc, Sex Asc, FirstName Asc"
	'==DEMO=====================================================================================
	'@ 下面这个例子将输出一个SQL语句：
	'@ AB.C.Print AB.db.wGetRecord("TestTable:fId,fName,fAge:5", Array("fSex:男","IsActive:1"), "fName Asc")
	'@ '输出结果为： Select Top 5 fId,fName,fAge From [TestTable] Where [fSex] = '男' And [IsActive] = 1
	'@ *****************************************************************************************

	Public Function wGetRecord(ByVal TableName,ByVal Condition,ByVal OrderField)
		Dim sql, FieldsList, ShowN, o, p
		FieldsList = "" : ShowN = 0
		Dim tb : tb = TableName : tb = DelFix(tb)
		o = Abx_Param(tb)
		If AB.C.Has(o(1)) Then
			tb = Trim(o(0)) : FieldsList = Trim(o(1)) : o = ""
			p = Abx_Param(FieldsList)
			If AB.C.Has(p(1)) Then
				FieldsList = Trim(p(0)) : ShowN = Int(Trim(p(1))) : p = ""
			Else
				If isNumeric(FieldsList) Then ShowN = Int(FieldsList) : FieldsList = ""
			End If
		End If
		sql = "Select "
		If ShowN > 0 Then sql = sql & "Top " & ShowN & " "
		sql = sql & AB.C.IIF(FieldsList <> "", FieldsList, "* ")
		sql = sql & " From [{prefix}" & tb & "]"
		If isArray(Condition) Then
			sql = sql & " Where " & ValueToSql(tb,Condition,1)
		Else
			If Condition <> "" Then sql = sql & " Where " & Condition
		End If
		If OrderField <> "" Then sql = sql & " Order By " & OrderField
		sql = FixSQL(sql)
		wGetRecord = sql
	End Function

	Public Function wGR(ByVal TableName,ByVal Condition,ByVal OrderField)
		wGR = wGetRecord(TableName, Condition, OrderField)
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.GetRecordBySql
	'@ 语  法:  Set rs = AB.db.GetRecordBySql(sql)
	'@ 别  名:  Set rs = AB.db.GRS(sql)
	'@ 返  回:  Recordset (记录集对象) 符合查询条件的记录集对象
	'@ 作  用:  根据SQL语句获取记录集
	'@ 			调用此方法将返回一个由参数SQL语句建立的记录集对象(Adodb.Recordset)。此对象可用 AB.db.C 方法关闭并释放。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ rs Variable (变量) 已定义未初始化的记录集对象
	'@ sql String (字符串) 用于生成记录集的SQL语句
	'==DEMO=====================================================================================
	'@ 下面的例子将返回一个由SQL语句建立的记录集：
	'@ Dim rs
	'@ Set rs = AB.db.GetRecordBySQL("Select a.Id, a.LastName, b.Group From [User] a Left Join [Depart] b On a.GroupId = b.GroupId")
	'@ *****************************************************************************************

	Public Function GetRecordBySQL(ByVal sql)
		On Error Resume Next
		sql = FixSQL(sql)
		Dim rs
		Select Case LCase(i_queryType)
			Case "1","command","cmd"
				Dim cmd : Set cmd = Server.CreateObject("ADODB.Command")
				With cmd
					.ActiveConnection = o_conn
					.CommandText = sql
					Set GetRecordBySQL = .Execute
				End With
				Set cmd = Nothing
			Case "2","rst2"
				Set GetRecordBySQL = Server.CreateObject("Adodb.Recordset")
				GetRecordBySQL.Open sql,o_conn,3,3
			Case "3","rst3"
				Set GetRecordBySQL = Server.CreateObject("Adodb.Recordset")
				GetRecordBySQL.Open sql,o_conn,1,3
			Case "4","exe","exec"
				Set GetRecordBySQL = o_conn.Execute(sql)
			Case Else '如值："0","recordset","rs","rst","rst1"
				Set GetRecordBySQL = Server.CreateObject("Adodb.Recordset")
				With GetRecordBySQL
					.ActiveConnection = o_conn
					.CursorType = 1
					.LockType = 1
					.Source = sql
					.Open
				End With
		End Select
		If IsObject(rs) Then : rs.close : Set rs = Nothing : End If
		Abx_DbQueryTimes = Abx_DbQueryTimes + 1
		If Err.number <> 0 Then
			AB.Error.Msg = "<br />" & sql
			AB.Error.Raise 11
			Err.Clear
		End If
		On Error Goto 0
	End Function

	Public Function GRS(ByVal sql)
		Set GRS = GetRecordBySQL(sql)
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.Run(sql, queryType)
	'@ 返  回:  Object/Array (RS对象或数据组)
	'@ 作  用:  数据库操作函数
	'==DESC=====================================================================================
	'@ 参数 sql : SQL语句
	'@ 参数 queryType : 查询类型 (缺省为 0|rs|rst1|rst|recordset)
	'@ 	queryType值: 0 | rs | recordset | rst1 | rst 	则返回(对象)类似 Rs.Open sql,Conn,1,1
	'@ 	queryType值: 1 | cmd | command 					则返回(对象)用 ADODB.Command 执行SQL语句
	'@ 	queryType值: 2 | rst2 							则返回(对象)类似 Rs.Open sql,Conn,3,3
	'@ 	queryType值: 3 | rst3 							则返回(对象)类似 Rs.Open sql,Conn,1,3
	'@ 	queryType值: 4 | exec | exe 					则返回(对象)类似 Conn.Execute(sql)
	'@ 	queryType值: 5 | arr | array 					则返回(数组)类似 Rs.getRows() 数据组
	'==DEMO=====================================================================================
	'@ Dim sql : sql = "SELECT * FROM [table] where id = 10"
	'@ Dim Rs : Set Rs = AB.db.Run(sql,"rs")
	'@ If Not Rs.Eof Then AB.C.Print Rs("id")
	'@ *****************************************************************************************

	Public Function Run(byval sql,byval queryType)
		On Error Resume Next
		sql = FixSQL(sql)
		Dim rs
		If Trim(queryType)="" Or IsNull(queryType) Then queryType = 0
		Select Case LCase(queryType)
			Case "0","recordset","rs","rst","rst1"
				Set Run = Server.CreateObject("Adodb.Recordset")
				Run.Open sql,o_conn,1,1
			Case "1","command","cmd"
				Set Run = DoExecute(sql)
			Case "2","rst2"
				Set Run = Server.CreateObject("Adodb.Recordset")
				Run.Open sql,o_conn,3,3
			Case "3","rst3"
				Set Run = Server.CreateObject("Adodb.Recordset")
				Run.Open sql,o_conn,1,3
			Case "4","exe","exec"
				Set Run = o_conn.Execute(sql)
			Case "5","arr","array"
				Set rs = Server.CreateObject("Adodb.Recordset")
				rs.Open sql,o_conn,1,1
				IF Not rs.Eof Then
					IF i_fetchCount > 0 Then Run = rs.getRows(i_fetchCount) Else Run = rs.getRows() : End IF
				End IF
			Case Else
				Set Run = Server.CreateObject("Adodb.Recordset")
				Run.Open sql,o_conn,1,1
		End Select
		If IsObject(rs) Then : rs.close : Set rs = Nothing : End If
		Abx_DbQueryTimes = Abx_DbQueryTimes + 1
		If Err.number <> 0 Then
			AB.Error.Msg = "<br />" & sql
			AB.Error.Raise 11
			Err.Clear
		End If
		On Error Goto 0
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.Json
	'@ 语  法:  AB.db.Json rs, name[:totalName][:notjs]
	'@ 返  回:  String (字符串) Json格式的字符串
	'@ 作  用:  根据记录集生成Json格式数据
	'@ 			调用此方法将用一个记录集对象包含的记录生成Json格式的数据。
	'@ 			如果省略notjs参数，则此方法生成的Json数据将经过 AB.C.jsEncode 方法转义特殊字符和中文字符，可直接用于AJAX数据的传递。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ rs Recordset (记录集对象) 要生成Json格式数据的记录集对象
	'@ name String (字符串) 该Json数据在Javascript中的名称
	'@ totalName(可选) String (字符串) 如果不省略此参数，则会在生成的Json字符串中添加一个名称为该参数的表示总记录数的项
	'@ notjs(可选) String (字符串) 此参数为固定字符串"notjs",如不省略此参数，则输出的Json字符串中不会将中文进行编码
	'==DEMO=====================================================================================
	'@ 下面的例子说明了此方法的用法：
	'@ Dim rs
	'@ Set rs = AB.db.GetRecord("TestTable:ID,Name:3","ID<20","ID Desc")
	'@ AB.C.Print AB.db.Json(rs,"users")
	'@ 上面的程序将输出下面的代码：
	'@ {"users":[{"ID":19,"name":"\u738B\u4E8C\u575B"},{"ID":18,"name":"\u4ED8\u5C0F\u5F3A"},{"ID":17,"name":"\u97E9\u8001\u4E94"}]}
	'@
	'@ 如果不省略 totalName 参数，例如：
	'@ AB.C.Print AB.db.Json(rs,"users:total")
	'@ 则上面的程序将输出下面的代码：
	'@ {"total":3,"users":[{"ID":18,"name":"\u738B\u4E8C\u575B"},{"ID":21,"name":"\u4ED8\u5C0F\u5F3A"},{"ID":22,"name":"\u97E9\u8001\u4E94"}]}
	'@
	'@ 如果不省略 notjs 字符串参数，则不会编码其中的中文字符，例如：
	'@ AB.C.Print AB.db.Json(rs,"users::notjs")
	'@ 会输出下面的代码，可以用于Flash等不需要编码中文的场合：
	'@ {"users":[{"ID":18,"name":"王二坛"},{"ID":21,"name":"付小强"},{"ID":22,"name":"韩老五"}]}
	'@ *****************************************************************************************

	Public Function Json(ByVal jRs, ByVal jName)
		On Error Resume Next
		Dim tmpStr, rs, fi, o, totalName, total, tName, tValue, notjs
		o = Abx_Param(jName)
		notjs = False
		If AB.C.Has(o(1)) Then
			jName = o(0) : totalName = o(1)
			o = Abx_Param(totalName)
			If AB.C.Has(o(1)) Then
				totalName = o(0) : notjs = LCase(o(1)) = "notjs"
			End If
		End If
		Set rs = jRs.Clone
		AB.Use "JSON"
		Set o = AB.Json.New(0)
		If notjs Then o.StrEncode = False
		total = 0
		If AB.C.Has(rs) Then
			total = Me.RsCount(rs)
			If AB.C.Has(totalName) Then o(totalName) = total
			o(jName) = AB.Json.New(1)
			While Not rs.Eof
				o(jName)(Null) = AB.Json.New(0)
				For Each fi In rs.Fields
					o(jName)(Null)(fi.Name) = fi.Value
				Next
				rs.MoveNext
			Wend
		End If
		tmpStr = o.JsString
		Set o = Nothing
		rs.Close() : Set rs = Nothing
		If Err.number <> 0 Then AB.Error.Raise 15
		Json = tmpStr
		On Error Goto 0
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.RandStr
	'@ 语  法:  AB.db.RandStr charNumber[:range] | min-max | string[:range], table:field
	'@ 返  回:  String (字符串) 返回随机字符串
	'@ 作  用:  生成一个不重复的指定长度的随机字符串
	'@ 			调用此方法将生成一个指定长度的且不与数据库中已经存在的字段的值重复的随机字符串，使用时根据参数的不同有三种用法：
	'@ 			第一，用charNumber和range参数将生成一个指定长度的不重复的随机字符串，其中包含大小写字母及数字。如果指定了range范围，则在指定的字符范围内生成随机字符串。
	'@ 			第二，用min和max参数将生成一个不重复的随机数，大小在min和max之间。
	'@ 			第三，用字符串作为参数，在字符串中嵌入形如“<n>”和“<min-max>”，可以在字符串内生成相应位数的不重复的随机字符串和随机数。
	'@ 			如果同时指定了range范围，则此字符串内所有的随机字符串均在指定的字符范围内生成。如果字符串内本身含有“:”、“<”、“>”，则需要先在前面加“\”进行转义。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ charNumber Integer (整数) 要生成的字符串的长度
	'@ range(可选) String (字符串) 要生成的字符串的范围，若指定此参数，则随机字符中这些字符中产生
	'@ min Integer (整数) 生成随机数的最小值，必须和max配合使用
	'@ max Integer (整数) 生成随机数的最大值，必须和min配合使用
	'@ string String (字符串) 要将随机字符串嵌入的字符串，可以嵌入随机字符串和随机数字：
	'@  - 用"<n>"表示字符串中要加入的随机字符串的位数
	'@  - 用"<min-max>"表示字符串中要加入的随机数字的最大值和最小值
	'@ table String (字符串) 要检测重复的数据表名
	'@ field String (字符串) 要检测重复的字段名
	'==DEMO=====================================================================================
	'@ 下面的例子说明了此方法的用法：
	'@ AB.C.PrintCn AB.db.RandStr(12,"Table:Field") '生成一个12位的不与表中已有字段重复的随机字符串
	'@ AB.C.PrintCn AB.db.RandStr("100000-999999","Table:Sort") '生成一个6位的不与表中已有字段重复的随机数
	'@ '生成一个包含特殊字符的不与表中已有字段重复的8位数随机密码
	'@ AB.C.PrintCn AB.db.RandStr("8:0123456789abcdefghijklmnopqrstuvwxyz~!@#$%^&*_-+=","Table:PassWord")
	'@ AB.C.PrintCn AB.db.RandStr("#<6>:0123456789ABCDEF","Table:Color") '在字符串内生成一个不与表中已有字段重复的随机的16进制颜色代码
	'@ AB.C.PrintCn AB.db.RandStr("{<8>-<4>-<4>-<4>-<12>}:0123456789ABCDEF","Table:CLSID") '生成一个不与表中已有字段重复的CLSID(类ID)
	'@ AB.C.PrintCn AB.db.RandStr("CN-\<86\>-<6>-<10000-99999>","Table:Code") '生成一个自定义的包含固定编码部分的不与表中已有字段重复的随机编码
	'@ AB.C.PrintCn AB.db.RandStr("<1-2999>","Table:DoorNo") '生成一个3000以内的不与表中已有字段重复的编号
	'@ *****************************************************************************************

	Public Function RandStr(length,TableField)
		On Error Resume Next
		Dim tb, fi, tmpStr, rs
		tb = Abx_Param(TableField)(0)
		fi = Abx_Param(TableField)(1)
		tb = DelFix(tb)
		tmpStr = AB.C.RandStr(length)
		Do While (True)
			Set rs = GR(tb&":"&fi&":1",fi&"='"&tmpStr&"'","")
			If Not rs.Bof And Not rs.Eof Then
				tmpStr = AB.C.RandStr(length)
			Else
				RandStr = tmpStr
				Exit Do
			End If
			Close(rs)
		Loop
		If Err.number <> 0 Then AB.Error.Raise 16
		On Error Goto 0
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.Rand
	'@ 语  法:  AB.db.Rand min, max, table:field
	'@ 返  回:  Integer (整数) 返回一个在min和max之间的数
	'@ 作  用:  生成一个不重复的随机数
	'@ 			调用此方法将生成一个在min和max之间且不与数据库中已经存在的某字段的值重复的随机数。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ min Integer (整数) 随机数最小值
	'@ max Integer (整数) 随机数最大值
	'@ table String (字符串) 要检测的数据表名
	'@ field String (字符串) 要检测的字段名
	'==DEMO=====================================================================================
	'@ AB.C.Print AB.db.Rand(10000,99999,"TestTable:ProID")
	'@ 上面的代码将生成一个5位随机数，且不与TestTable表内的ProId列已经存在的数值重复
	'@ 如果在这随机范围之间都与TestTable表内的ProId列已经存在的数值重复，则返回0
	'@ *****************************************************************************************

	Public Function Rand(min,max,TableField)
		On Error Resume Next
		Dim tb, fi, tmpInt, rs, rs2, minid, maxid
		tb = Abx_Param(TableField)(0)
		fi = Abx_Param(TableField)(1)
		tb = DelFix(tb)
		maxid = Me.MaxID(tb&":"&fi)
		minid = Me.MinID(tb&":"&fi)
		If maxid < min and maxid < max Then
			Rand = AB.C.Rand(min,max)
			Exit Function
		End If
		Dim k : k = min
		Do While (True)
			Set rs2 = GR(tb&":"&fi&":1",Array(fi&":"&k),"")
			If Not (rs2.Bof And rs2.Eof) Then
				If k >= Max Then
					Rand = 0
					Exit Do
				End If
			End If
			k = k + 1
			Close(rs2)
			Set rs = GR(tb&":"&fi&":1",Array(fi&":"&tmpInt),"")
			If Not (rs.Bof And rs.Eof) Then
				tmpInt = AB.C.Rand(min,max)
			Else
				Rand = tmpInt
				Exit Do
			End If
			Close(rs)
		Loop
		If Err.number <> 0 Then AB.Error.Raise 17
		On Error Goto 0
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.RandID
	'@ 语  法:  AB.db.RandID min, max, table:field
	'@ 返  回:  Integer (整数) 返回一个在min和max之间且存在与table表的field字段有存在相等数值的随机数
	'@ 作  用:  生成一个不重复的随机数
	'@ 			调用此方法将生成一个在min和max之间且存在与table表的field字段有存在相等数值的随机数。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ min Integer (整数) 随机数最小值
	'@ max Integer (整数) 随机数最大值
	'@ table String (字符串) 要检测的数据表名
	'@ field String (字符串) 要检测的字段名
	'==DEMO=====================================================================================
	'@ AB.C.Print AB.db.RandID(10000,99999,"TestTable:ProID")
	'@ 上面的代码将生成一个在TestTable表内的ProId列已经存在的数值内的5位随机数
	'@ 如果在这随机范围之间没有一个在TestTable表内的ProId列存在的数值相等，则返回0
	'@ *****************************************************************************************

	Public Function RandID(min,max,TableField)
		On Error Resume Next
		Dim tb, fi, tmpInt, rs, rs2, minid, maxid
		tb = Abx_Param(TableField)(0)
		fi = Abx_Param(TableField)(1)
		tb = DelFix(tb)
		maxid = Me.MaxID(tb&":"&fi)
		minid = Me.MinID(tb&":"&fi)
		If maxid < min and maxid < max Then
			RandID = 0
			Exit Function
		End If
		minid = AB.C.IIF(minid <= min, min, minid)
		maxid = AB.C.IIF(maxid >= max, max, maxid)
		tmpInt = 0
		Dim k : k = min
		Do While (True)
			Set rs2 = GR(tb&":"&fi&":1",Array(fi&":"&k),"")
			If rs2.Bof And rs2.Eof Then
				If k >= Max Then
					RandID = 0
					Exit Do
				End If
			End If
			k = k + 1
			Close(rs2)
			Set rs = GR(tb&":"&fi&":1",Array(fi&":"&tmpInt),"")
			If Not (rs.Bof And rs.Eof) Then
				RandID = tmpInt
				Exit Do
			Else
				tmpInt = AB.C.Rand(minid,maxid)
			End If
			Close(rs)
		Loop
		If Err.number <> 0 Then AB.Error.Raise 10
		On Error Goto 0
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.MaxID
	'@ 语  法:  AB.db.MaxID table:field
	'@ 返  回:  Integer (整数) 返回Table表的field字段最大值
	'@ 作  用:  获取Table表的field字段数据的最大值
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ table String (字符串) 要检测的数据表名
	'@ field String (字符串) 要检测的字段名
	'==DEMO=====================================================================================
	'@ AB.C.Print AB.db.MaxID("TestTable:ProID")
	'@ *****************************************************************************************

	Public Function MaxID(TableField)
		On Error Resume Next
		Dim tb, fi, tmpInt, rs, sql
		tb = Abx_Param(TableField)(0)
		fi = Abx_Param(TableField)(1)
		tb = DelFix(tb)
		tmpInt = 0
		If AB.C.IsNul(fi) Then fi = tbAutoID(tb)
		If Not AB.C.IsNul(fi) Then
			sql = "SELECT Max("&fi&") FROM [{prefix}"&tb&"]"
			sql = FixSQL(sql)
			Set rs = GRS(sql)
			If Not (rs.Bof And rs.Eof) Then tmpInt = rs(0)
		End If
		MaxID = tmpInt
		If Err.number <> 0 Then AB.Error.Raise 9
		On Error Goto 0
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.MinID
	'@ 语  法:  AB.db.MinID table:field
	'@ 返  回:  Integer (整数) 返回Table表的field字段最小值
	'@ 作  用:  获取Table表的field字段数据的最小值
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ table String (字符串) 要检测的数据表名
	'@ field String (字符串) 要检测的字段名
	'==DEMO=====================================================================================
	'@ AB.C.Print AB.db.MinID("TestTable:ProID")
	'@ *****************************************************************************************

	Public Function MinID(TableField)
		On Error Resume Next
		Dim tb, fi, tmpInt, rs, sql
		tb = Abx_Param(TableField)(0)
		fi = Abx_Param(TableField)(1)
		tb = DelFix(tb)
		tmpInt = 0
		If AB.C.IsNul(fi) Then fi = tbAutoID(tb)
		If Not AB.C.IsNul(fi) Then
			sql = "SELECT Min("&fi&") FROM [{prefix}"&tb&"]"
			sql = FixSQL(sql)
			Set rs = GRS(sql)
			If Not (rs.Bof And rs.Eof) Then tmpInt = rs(0)
		End If
		MinID = tmpInt
		If Err.number <> 0 Then AB.Error.Raise 9
		On Error Goto 0
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.GetRecordDetail
	'@ 语  法:  Set rs = AB.db.GetRecordDetail(table, condition)
	'@ 别  名:  Set rs = AB.db.GRD(table, condition)
	'@ 返  回:  Recordset (记录集对象) 符合查询条件的记录集对象
	'@ 作  用:  根据查询条件获取记录集
	'@ 			调用此方法将返回一个由查询条件建立的记录集对象(Adodb.Recordset)。此对象可用 AB.db.C 方法关闭并释放。
	'@ 			功能有些类似于AB.db.GetRecordBySQL(sql)方法
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ rs Variable (变量) 已定义未初始化的RecordSet对象
	'@ table String (字符串) 数据表名称
	'@ condition Array (数组) 或 String (字符串) 查询条件，不包含“Where”，如果是数组应遵循"数组参数约定"
	'==DEMO=====================================================================================
	'@ 下面的例子将返回一个由记录集：
	'@ Dim rs : Set rs = AB.db.GetRecordDetail("TestTable", "ProID = 18")
	'@ ab.trace(rs)
	'@ *****************************************************************************************

	Public Function GetRecordDetail(ByVal TableName,ByVal Condition)
		Dim tb : tb = TableName : tb = DelFix(tb)
		Dim sql : sql = "Select * From [{prefix}" & tb & "] Where " & ValueToSql(tb,Condition,1)
		sql = FixSQL(sql)
		GetRecordDetail = sql
		Set GetRecordDetail = GRS(sql)
	End Function

	Public Function GRD(ByVal TableName,ByVal Condition)
		Set GRD = GetRecordDetail(TableName, Condition)
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.GetRandRecord
	'@ 语  法:  Set rs = AB.db.GetRandRecord(table:fields:number, condition)
	'@ 别  名:  Set rs = AB.db.GRR(table:fields:number, condition)
	'@ 返  回:  Recordset (记录集对象) 符合查询条件的记录集对象
	'@ 作  用:  获取一个指定数量的随机数据记录集
	'@ 			用此方法可以返回一个从数据库中随机抽取的数据组成的记录集，记录集的数量和条件都在参数中指定即可。参数中必须指定数据表的ID字段的名称和数量。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ rs Variable (变量) 已定义未初始化的RecordSet对象
	'@ table String (字符串) 数据表名称
	'@ fields String (字符串) 字段名称，用逗号隔开，如果要取多个字段，必须把ID字段名称放在第1个。如果只写ID字段名称，则是取全部字段的数据
	'@ number Integer (整数) 要取得的随机记录集的数量
	'@ condition Array (数组) 或 String (字符串) 查询条件，不包含“Where”，如果是数组应遵循"数组参数约定"
	'==DEMO=====================================================================================
	'@ 下面的例子将返回一个由记录集：
	'@ Dim rs : Set rs = AB.db.GetRandRecord("UserInfo:ID:10","Sex='男' And Age>=20")
	'@ ab.trace(rs)
	'@ 上面的代码从UserInfo表中取得了10条随机的性别为男、年龄在20岁以上的人员信息记录。
	'@ *****************************************************************************************

	Public Function GetRandRecord(ByVal TableName,ByVal Condition)
		Dim sql,o,p,fi,IdField,showN,where
		Dim tb : tb = TableName : tb = DelFix(tb)
		o = Abx_Param(tb)
		If AB.C.Has(o(1)) Then
			tb = o(0)
			p = Abx_Param(o(1))
			If AB.C.isNul(p(1)) Then
				AB.Error.Raise 18
				Exit Function
			Else
				fi = p(0) : showN = p(1)
				If Instr(fi,",")>0 Then
					IdField = Trim(Left(fi,Instr(fi,",")-1))
				Else
					IdField = fi : fi = "*"
				End If
			End If
		Else
			AB.Error.Raise 19
			Exit Function
		End If
		Condition = AB.C.IIF(AB.C.isNul(Condition),""," Where " & ValueToSql(tb,Condition,1))
		sql = "Select Top " & showN & " " & fi & " From [{prefix}"&tb&"]" & Condition
		Select Case s_dbType
			Case "ACCESS","ACC","" : Randomize
				sql = sql & " Order By Rnd(-(" & IdField & "+" & Rnd() & "))"
			Case "MSSQL","SQL"
				sql = sql & " Order By newid()"
			Case "MYSQL"
				sql = "Select " & fi & " From {prefix}"&tb&"" & Condition & " Order By rand() limit " & showN
			Case "ORACLE"
				sql = "Select " & fi & " From (Select " & fi & " From {prefix}"&tb&" Order By dbms_random.value) " & AB.C.IIF(AB.C.isNul(Condition),"Where",Condition & " And") & " rownum < " & Int(showN)+1
		End Select
		sql = FixSQL(sql)
		Set GetRandRecord = GRS(sql)
	End Function

	Public Function GRR(ByVal TableName,ByVal Condition)
		Set GRR = GetRandRecord(TableName,Condition)
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.AddRecord
	'@ 语  法:  [result = ]AB.db.AddRecord(table[:idField], valueArray)
	'@ 别  名:  [result = ]AB.db.AR(table[:idField], valueArray)
	'@ 返  回:  Integer (整数)
	'@ 			当省略idField参数时，添加成功返回1， 添加失败返回0
	'@ 			当存在idField参数时，添加成功返回新添加的记录的ID号，添加失败返回0
	'@ 作  用:  调用此方法将往指定数据表中插入一条新的记录。如果要插入的字段为数字型但要插入的值为空，则将把NULL值插入该字段。
	'@ 			如果要查看该方法生成的SQL语句，可用 AB.db.wAddRecord 方法。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ parame result(可选) Variable (变量) 返回值，已定义的变量
	'@ table String (字符串) 要添加记录的数据表名
	'@ idField(可选) String (字符串) ID字段的名称，如果不省略则添加成功后返回新ID的值
	'@ valueArray Array (数组) 要插入表的字段和值，只能是数组且应遵循"数组参数约定"
	'==DEMO=====================================================================================
	'@ 下面的例子添加了一条新的记录，需要注意的是，遵循"数组参数约定"，数组参数的值是不用考虑数据类型的：
	'@ Dim fName, fSex, fWorkYear, fBirth
	'@ fName = "王二坛"
	'@ fSex = "男"
	'@ fWorkYear = 12
	'@ fBirth = Cdate("1981-10-23")
	'@ '直接添加记录
	'@ Call AB.db.AddRecord("TestTable",Array("Name:"&fName, "Sex:"&fSex, "WorkYear:"&fWorkYear, "Birthday:"&fBirth, "IsActive:True"))
	'@ '添加记录同时返回新的UserId值
	'@ Dim result
	'@ result = AB.db.AddRecord("TestTable:UserId",Array("Name:"&fName, "Sex:"&fSex, "WorkYear:"&fWorkYear, "Birthday:"&fBirth, "IsActive:True"))
	'@ If result<>0 Then
	'@     Response.Write("添加记录成功！此记录的自动编号UserId为" & result)
	'@ End If
	'@ *****************************************************************************************

	Public Function AddRecord(ByVal TableName,ByVal ValueList)
		On Error Resume Next
		Dim tb : tb = TableName : tb = DelFix(tb)
		Dim o,s : o = Abx_Param(tb)
		If AB.C.Has(o(1)) Then tb = o(0)
		'AddRecord = AutoID(o(0)&":"&o(1))
		s = wAddRecord(tb,ValueList)
		DoExecute s
		If Err.number <> 0 Then
			If Err Then s = s & "<br>【错误代码】: " & Err.Number & "<br>【错误信息】: " & Err.Description
			AB.Error.Msg = "<br />" & s
			AB.Error.Raise 20
			AddRecord = 0
			Exit Function
		End If
		If AB.C.Has(o(1)) Then
			AddRecord = MaxID(o(0)&":"&o(1))
		Else
			AddRecord = 1
		End If
		On Error Goto 0
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.wAddRecord
	'@ 语  法:  AB.db.wAddRecord table[:idField], valueArray
	'@ 别  名:  AB.db.wAR table[:idField], valueArray
	'@ 返  回:  String (字符串) SQL语句
	'@ 作  用:  调用此方法将返回一个由指定条件生成的SQL插入语句。如果调用 AB.db.AddRecord 方法时失败时，可用此方法输出相关的SQL语句进行排错。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ table String (字符串) 要添加记录的数据表名
	'@ idField(可选) String (字符串) ID字段的名称，如果不省略则添加成功后返回新ID的值
	'@ valueArray Array (数组) 要插入表的字段和值，只能是数组且应遵循"数组参数约定"
	'==DEMO=====================================================================================
	'@ 下面的例子添加了一条新的记录，需要注意的是，遵循"数组参数约定"，数组参数的值是不用考虑数据类型的：
	'@ Dim fName, fSex, fWorkYear, fBirth
	'@ fName = "王二坛"
	'@ fSex = "男"
	'@ fWorkYear = 12
	'@ fBirth = Cdate("1986-01-01")
	'@ AB.C.Print AB.db.wAddRecord("TestTable",Array("Name:"&fName, "Sex:"&fSex, "WorkYear:"&fWorkYear, "Birthday:"&fBirth, "IsActive:True"))
	'@ '输出结果：Insert Into [TestTable] (Name,Sex,WorkYear,Birthday,IsActive) Values ('王二坛','男',12,'1986-01-01',1)
	'@ *****************************************************************************************

	Public Function wAddRecord(ByVal TableName,ByVal ValueList)
		Dim sql, TempFiled, TempValue, o
		Dim tb : tb = TableName : tb = DelFix(tb)
		o = Abx_Param(tb) : If AB.C.Has(o(1)) Then tb = o(0)
		TempFiled = ValueToSql(tb,ValueList,2)
		TempValue = ValueToSql(tb,ValueList,3)
		sql = "Insert Into [{prefix}" & tb & "] (" & TempFiled & ") Values (" & TempValue & ")"
		sql = FixSQL(sql)
		wAddRecord = sql
	End Function

	Public Function AR(ByVal TableName,ByVal ValueList)
		AR = AddRecord(TableName,ValueList)
	End Function

	Public Function wAR(ByVal TableName,ByVal ValueList)
		wAR = wAddRecord(TableName,ValueList)
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.UpdateRecord
	'@ 语  法:  [result = ]AB.db.UpdateRecord(table, condition, value)
	'@ 别  名:  [result = ]AB.db.UR(table, condition, value)
	'@ 返  回:  Numeric (数值) 更新成功将返回数值1，如果更新失败将返回数值0
	'@ 作  用:  根据指定条件更新记录
	'@ 			调用此方法将按指定的条件更新一条或多条记录，如果更新成功将返回数值1，如果更新失败将返回数值0。 要查询此方法生成的SQL语句，可用 AB.db.wUpdateRecord 方法。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ result(可选) Numeric (数值) 返回值，更新成功返回1，更新失败返回0
	'@ table String (字符串) 数据表名称
	'@ condition Array (数组) 或 String (字符串) 更新条件，如果是数组应遵循"数组参数约定"
	'@ value Array (数组) 或 String (字符串) 更新的字段及值，如果是数组应遵循"数组参数约定"
	'==DEMO=====================================================================================
	'@ 下面的例子按指定的条件更新了一条记录：
	'@ Dim fName, fWorkYear
	'@ fName = "王三坛"
	'@ fWorkYear = 10
	'@ Dim result
	'@ result = AB.db.UpdateRecord("TestTable", "UId = 1308", Array("Name:"&fName, "WorkYear:"&fWorkYear))
	'@ If result<>0 Then
	'@     Response.Write("更新数据成功！")
	'@ End If
	'@ *****************************************************************************************

	Public Function UpdateRecord(ByVal TableName,ByVal Condition,ByVal ValueList)
		On Error Resume Next
		Dim tb : tb = TableName : tb = DelFix(tb)
		Dim s : s = wUpdateRecord(tb,Condition,ValueList)
		DoExecute s
		If Err.number <> 0 Then
			AB.Error.Msg = "<br />" & s
			AB.Error.Raise 21
			UpdateRecord = 0
			Exit Function
		End If
		UpdateRecord = 1
		On Error Goto 0
	End Function

	Public Function UR(ByVal TableName,ByVal Condition,ByVal ValueList)
		UR = UpdateRecord(TableName, Condition, ValueList)
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.wAddRecord
	'@ 语  法:  AB.db.wAddRecord table[:idField], valueArray
	'@ 别  名:  AB.db.wAR table[:idField], valueArray
	'@ 返  回:  String (字符串) SQL语句
	'@ 作  用:  生成添加一个新的纪录的SQL语句
	'@ 			调用此方法将返回一个由指定条件生成的SQL插入语句。如果调用 AB.db.AddRecord 方法时失败时，可用此方法输出相关的SQL语句进行排错。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ table String (字符串) 要添加记录的数据表名
	'@ idField(可选) String (字符串) ID字段的名称，如果不省略则添加成功后返回新ID的值
	'@ valueArray Array (数组) 要插入表的字段和值，只能是数组且应遵循"数组参数约定"
	'==DEMO=====================================================================================
	'@ 下面的例子按指定的条件更新了一条记录：
	'@ Dim fName, fSex, fWorkYear, fBirth
	'@ fName = "王二坛"
	'@ fSex = "男"
	'@ fWorkYear = 12
	'@ fBirth = Cdate("1986-01-01")
	'@ AB.C.Print AB.db.wAddRecord("TestTable",Array("Name:"&fName, "Sex:"&fSex, "WorkYear:"&fWorkYear, "Birthday:"&fBirth, "IsActive:True"))
	'@ 输出结果为：
	'@ Insert Into [TestTable] (Name,Sex,WorkYear,Birthday,IsActive) Values ('王二坛','男',12,'1986-01-01',1)
	'@ *****************************************************************************************

	Public Function wUpdateRecord(ByVal TableName,ByVal Condition,ByVal ValueList)
		Dim sql
		Dim tb : tb = TableName : tb = DelFix(tb)
		sql = "Update [{prefix}"&tb&"] Set "
		sql = sql & ValueToSql(tb,ValueList,0)
		If AB.C.Has(Condition) Then sql = sql & " Where " & ValueToSql(tb,Condition,1)
		sql = FixSQL(sql)
		wUpdateRecord = sql
	End Function

	Public Function wUR(ByVal TableName,ByVal Condition,ByVal ValueList)
		wUR = wUpdateRecord(TableName, Condition, ValueList)
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.DeleteRecord
	'@ 语  法:  [result = ]AB.db.DeleteRecord(table,field:valueList|condition|arrayCondition)
	'@ 别  名:  [result = ]AB.db.DR(table,field:valueList|condition|arrayCondition)
	'@ 返  回:  Integer (整数) 删除成功返回数值1，删除失败返回数值0
	'@ 作  用:  根据指定条件删除记录
	'@ 			调用此方法可以按指定的条件删除一条或多条记录，如果删除成功返回数值1，如果删除失败返回数值0。
	'@ 			指定删除的条件有三种，一种是按(字段名：值序列)的方式，一种是字符串条件方式，还有一种是数组条件方式。
	'@ 			如果要查看该方法生成的SQL语句，可用 AB.db.WDeleteRecord 方法生成。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ result(可选) Variable (变量) 返回值，删除成功返回1，删除失败返回0
	'@ table String (字符串) 数据表名称
	'@ field String (字符串) 数据表的条件字段的名称
	'@ valueList String (字符串) 由逗号隔开的多个值，field列的值在此列表中的项目将被删除
	'@ condition String (字符串) 删除条件
	'@ arrayCondition Array (数组) 数组删除条件，格式应遵循 “数组参数约定”
	'==DEMO=====================================================================================
	'@ 下面的例子按指定的条件更新了一条记录：
	'@ Dim ids, result
	'@ ids = Request.Form("selectid") '可以假设这里获取的值是 12, 34, 256, 314 (模拟复选框提交的值)
	'@ result = AB.db.DeleteRecord("TestTable", "UId:" & ids)
	'@ If result<>0 Then
	'@     Response.Write("删除数据成功！")
	'@ End If
	'@ *****************************************************************************************

	Public Function DeleteRecord(ByVal TableName,ByVal Condition)
		On Error Resume Next
		Dim tb : tb = TableName : tb = DelFix(tb)
		Dim s : s = wDeleteRecord(tb,Condition)
		DoExecute s
		If Err.number <> 0 Then
			AB.Error.Msg = "<br />" & s
			AB.Error.Raise 22
			DeleteRecord = 0
			Exit Function
		End If
		DeleteRecord = 1
		On Error Goto 0
	End Function

	Public Function DR(ByVal TableName,ByVal Condition)
		DR = DeleteRecord(TableName, Condition)
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.wDeleteRecord
	'@ 语  法:  AB.db.wDeleteRecord(table,field:valueList|condition|arrayCondition)
	'@ 别  名:  AB.db.wDR(table,field:valueList|condition|arrayCondition)
	'@ 返  回:  String (字符串) SQL删除语句
	'@ 作  用:  生成删除符合条件的纪录的SQL语句
	'@ 			调用此方法将返回一个由指定条件生成的SQL删除语句。如果调用 AB.db.DeleteRecord 方法时失败时，可用此方法输出相关的SQL语句进行排错。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ table String (字符串) 数据表名称
	'@ field String (字符串) 数据表的条件字段的名称
	'@ valueList String (字符串) 由逗号隔开的多个值，field列的值在此列表中的项目将被删除
	'@ condition String (字符串) 删除条件
	'@ arrayCondition Array (数组) 数组删除条件，格式应遵循 "数组参数约定"
	'==DEMO=====================================================================================
	'@ 下面的例子将输出相应的SQL语句：
	'@ Response.Write(AB.db.wDR("TestTable", Array("isActive:1","FirstName:Tom")))
	'@ 输出结果为：
	'@ Delete From [TestTable] Where [UId] In (11,24,56,129)
	'@ Delete From [TestTable] Where [isActive] = 1 And [FirstName] = 'Tom'
	'@ *****************************************************************************************

	Public Function wDeleteRecord(ByVal TableName,ByVal Condition)
		Dim IDFieldName, IDValues, Sql, p : IDFieldName = "" : IDValues = ""
		Dim tb : tb = TableName : tb = DelFix(tb)
		If Not isArray(Condition) Then
			p = Abx_Param(Condition)
			If AB.C.Has(p(1)) Then
				IDFieldName = p(0)
				If Instr(IDFieldName," ")=0 Then
					IDValues = p(1)
				Else
					IDFieldName = ""
				End If
			End If
		End If
		Sql = "Delete From [{prefix}"&tb&"] Where " & AB.C.IIF(IDFieldName="", ValueToSql(tb,Condition,1), "["&IDFieldName&"] In (" & IDValues & ")")
		Sql = FixSQL(Sql)
		wDeleteRecord = Sql
	End Function

	Public Function wDR(ByVal TableName,ByVal Condition)
		wDR = wDeleteRecord(TableName, Condition)
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.ReadTable
	'@ 语  法:  AB.db.ReadTable table, condition, targetField
	'@ 别  名:  AB.db.RT table, condition, targetField
	'@ 返  回:  "" (空字符串) 或 Array (数组) 或 String (字符串)
	'@ 			如果targetField参数超过1个字段，返回数组，否则返回字符串。
	'@ 			如果没有符合条件的记录返回空字符串。
	'@ 作  用:  根据指定条件获取某条纪录中的其他字段的内容
	'@ 			调用此方法将返回数据表中符合指定条件的某一条记录中的一个字段或多个字段的值。
	'@ 			如果targetField参数获取的字段超过1个，返回的值将是一个数组。如果没有获取到符合条件的值，将返回空值。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ table String (字符串) 数据表名称
	'@ condition Array (数组) 或 String (字符串) 查询条件，如果是数组应遵循"数组参数约定"
	'@ targetField String (字符串) 单个字段名或者由逗号隔开的多个字段名
	'==DEMO=====================================================================================
	'@ 下面的例子说明了这个方法在获取一个字段值时的用法：
	'@ Dim uid, result
	'@ uid = 102
	'@ result = AB.db.ReadTable("UserTable","UId=" & uid, "UserName")
	'@ If result<>"" Then
	'@ 	AB.C.Print("发布者：" & result)
	'@ Else
	'@ 	AB.C.Print("此ID没有对应的人员")
	'@ End If
	'@
	'@ 下面的例子则说明了在获取多个字段值时的用法：
	'@ Dim uid, result
	'@ uid = 102
	'@ result = AB.db.RT("UserTable","UId=" & uid, "UserName,UserSex,UserAge")
	'@ If AB.C.Has(result) Then '此处返回值可能是数组也可能是空值，所以用AB.C.IsNul方法检测是否为空值
	'@	AB.C.Print("发布者：" & result(0) & "<br /> 性别：" & result(1) & "<br /> 年龄：" &result(2))
	'@ Else
	'@ 	AB.C.Print("此ID没有对应的人员")
	'@ End If
	'@ *****************************************************************************************

	Public Function ReadTable(ByVal TableName,ByVal Condition,ByVal GetFieldNames)
		On Error Resume Next
		Dim rs,Sql,arrTemp,arrStr,TempStr,i
		Dim tb : tb = TableName : tb = DelFix(tb)
		TempStr = "" : arrStr = ""
		Sql = "Select "&GetFieldNames&" From [{prefix}"&tb&"] Where " & ValueToSql(tb,Condition,1)
		sql = FixSQL(sql)
		Set rs = GRS(Sql)
		If Not rs.Eof Then
			If Instr(GetFieldNames,",") > 0 Then
				arrTemp = Split(GetFieldNames,",")
				For i = 0 To Ubound(arrTemp)
					If i<>0 Then arrStr = arrStr & Chr(0)
					arrStr = arrStr & rs.Fields.Item(i).Value
				Next
				TempStr = Split(arrStr,Chr(0))
			Else
				TempStr = rs.Fields.Item(0).Value
			End If
		End If
		rs.close() : Set rs = Nothing
		If Err.number <> 0 Then AB.Error.Raise 23 : Err.Clear
		ReadTable = TempStr
		On Error Goto 0
	End Function

	Public Function RT(ByVal TableName,ByVal Condition,ByVal GetFieldNames)
		RT = ReadTable(TableName, Condition, GetFieldNames)
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.Close
	'@ 别  名:  AB.db.C
	'@ 语  法:  AB.db.Close object
	'@ 返  回:  无返回值
	'@ 作  用:  关闭数据连接或记录集对象并资源释放资源
	'@ 			调用此方法可以关闭由Set语法建立的数据连接或记录集对象。它等同于代码“object.Close():Set object = Nothing”。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ object Connection (数据库连接对象) 或 Recordset (记录集对象) 待关闭的数据连接对象或记录集对象
	'==DEMO=====================================================================================
	'@ 下面的例子关闭了一个记录集对象：
	'@ Dim rs
	'@ Set rs = AB.db.GetRecordDetail("TestTable","Id=1208")
	'@ '... ...
	'@ AB.db.C(rs)
	'@ *****************************************************************************************

	Public Function Close(ByRef o)
		On Error Resume Next
		If IsObject(o) Then 'Object
			o.Close() : Set o = Nothing
		ElseIF VarType(o) = 8 Then 'String
			Dim temp : temp = Eval("LCase(TypeName(o))")
			If temp = "connection" Then : Execute("If o.State = 1 Then o.Close()") : End If
			If temp = "recordset" Then : Execute("If o.State = 1 Then o.Close()") : End If
			Execute("Set "& o &" = Nothing")
		End If
		IF Err Then Err.Clear
		On Error Goto 0
	End Function

	Public Function C(ByRef o)
		On Error Resume Next
		If IsObject(o) Then 'Object
			o.Close() : Set o = Nothing
		ElseIF VarType(o) = 8 Then 'String
			Dim temp : temp = Eval("LCase(TypeName(o))")
			If temp = "connection" Then : Execute("If o.State = 1 Then o.Close()") : End If
			If temp = "recordset" Then : Execute("If o.State = 1 Then o.Close()") : End If
			Execute("Set "& o &" = Nothing")
		End If
		IF Err Then Err.Clear
		On Error Goto 0
	End Function

	'取得Rs的记录数
	Public Function RsCount(ByVal Rs)
		Dim n : n = 0
		If AB.C.IsRs(Rs) Then
			n = Rs.RecordCount
			If n=-1 Then
				n = Ubound(Rs.GetRows(),2)+1
			End If
		End If
		RsCount = n
    End Function

	'克隆Rs数据集
	Public Function CloneRs(ByVal Rs)
		On Error Resume Next
		Dim newRs
		If AB.C.IsRs(Rs) Then
			If Me.RsCount(Rs)>1000 Then '为保证效率，记录集数多的话直接使用rs.clone()方法
				Set newRs = CloneRs__(Rs,0) '即rs.Clone
			Else
				Set newRs = CloneRs__(Rs,1)
			End If
			Set CloneRs = newRs
		End If
		On Error Goto 0
	End Function

	'对Rs数据集应用Sort排序
	Public Function SortRs(ByVal Rs, ByVal sort)
		On Error Resume Next
		Dim newRs
		If AB.C.IsRs(Rs) Then
			'Set newRs = Me.CloneRs(Rs)
			Set newRs = CloneRs__(Rs,1) '注意：这个rs记录数一多，查询会变得很慢
			If Trim(sort)<>"" Then newRs.Sort = sort
			'If Err Then AB.Error.See()
			Set SortRs = newRs
		End If
		On Error Goto 0
	End Function

	'克隆Rs数据集
	Public Function CloneRs__(ByVal Rs, ByVal t)
		On Error Resume Next
		AB.Use "A"
		Dim newRs, i, j, f, v, a, b, pos : pos = 0
		If AB.C.IsRs(Rs) Then
			If t = 1 Then
				pos = Rs.AbsolutePosition '记录游标位置
				Set newRs = AB.C.NewRs()
				newRs.CursorLocation = 1
				newRs.CursorType = 3
				For i = 0 To Rs.Fields.Count-1
					newRs.Fields.Append Rs.Fields(i).Name, Rs.Fields(i).Type, Rs.Fields(i).DefinedSize, Rs.Fields(i).Attributes
				Next
				newRs.Open
				If Me.RsCount(Rs)>0 Then Rs.MoveFirst()
				Do While Not Rs.eof
					a = Array()
					b = Array()
					For j = 0 To Rs.Fields.Count-1
						f = Rs.Fields(j).Name
						v = Rs.Fields(j).Value
						If Not AB.A.InArray(Rs.Fields(j).Type, Array( 7, 135 )) Then
							'v = AB.C.IIF(AB.C.isNul(v),"",v)
						End If
						a = AB.A.Push(a, f)
						b = AB.A.Push(b, v)
					Next
					newRs.AddNew a, b
					newRs.Update()
					Rs.MoveNext
				Loop
				If Me.RsCount(newRs)>0 Then newRs.MoveFirst()
				If Me.RsCount(Rs)>0 Then Rs.MoveFirst()
				If pos>0 Then
					newRs.Move(pos-1)
					Rs.Move(pos-1)
				End If
			Else
				pos = Rs.AbsolutePosition '记录游标位置
				Set newRs = Rs.Clone()
				If pos>0 Then newRs.Move(pos-1)
			End If
			Set CloneRs__ = newRs
		End If
		On Error Goto 0
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.getRs
	'@ 语  法:  [Set rs = ]AB.db.getRs(sql)
	'@ 返  回:  Recordset (记录集对象)
	'@ 作  用:  执行指定的SQL语句并返回rs记录集对象(该rs对象只读，不可更新)
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ rs(可选) Variable (变量) 已定义未初始化的RecordSet对象
	'@ sql String (字符串) 待执行的SQL语句
	'==DEMO=====================================================================================
	'@ 下面的例子说明了此方法的用法：
	'@ '用此方法获取记录集(只读，不能更新)
	'@ Dim rs : Set rs = AB.db.getRs("Select * From User A Right Join Dept B On A.ID=B.UserID")
	'@ *****************************************************************************************

	Public Function getRs(ByVal sql)
		Set getRs = Server.CreateObject("Adodb.Recordset")
		getRs.Open sql,o_conn,1,1
		If Err.number <> 0 Then
			AB.Error.Msg = "<br />" & sql
			AB.Error.Raise 25
			Err.Clear
		End If
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.Rs
	'@ 语  法:  [Set rs = ]AB.db.Rs(sql)
	'@ 返  回:  Recordset (记录集对象)
	'@ 作  用:  执行指定的SQL语句并返回rs记录集对象(该rs对象可更新)
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ rs(可选) Variable (变量) 已定义未初始化的RecordSet对象
	'@ sql String (字符串) 待执行的SQL语句
	'==DEMO=====================================================================================
	'@ 下面的例子说明了此方法的用法：
	'@ '用此方法获取记录集(可更新)
	'@ Dim rs : Set rs = AB.db.Rs("Select * From User A Right Join Dept B On A.ID=B.UserID")
	'@ *****************************************************************************************

	Public Function Rs(ByVal sql)
		Set Rs = Server.CreateObject("Adodb.Recordset")
		Rs.Open sql,o_conn,1,3
		If Err.number <> 0 Then
			AB.Error.Msg = "<br />" & sql
			AB.Error.Raise 25
			Err.Clear
		End If
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.Exec
	'@ 语  法:  [Set rs = ]AB.db.Exec(sql)
	'@ 返  回:  Integer (整数) 或 Recordset (记录集对象)
	'@ 			如果是获取记录集操作，执行成功返回记录集对象，执行失败返回0
	'@ 			如果是执行SQL语句，执行成功返回1，执行失败返回0
	'@ 作  用:  执行指定的SQL语句
	'@ 			用此方法可以直接执行一句SQL语句，并返回成功信息
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ rs(可选) Variable (变量) 已定义未初始化的RecordSet对象
	'@ sql String (字符串) 待执行的SQL语句
	'==DEMO=====================================================================================
	'@ 下面的例子说明了此方法的用法：
	'@ '用此方法获取记录集
	'@ Dim rs
	'@ Set rs = AB.db.Exec("Select * From User A Right Join Dept B On A.ID=B.UserID")
	'@ '用此方法执行SQL
	'@ AB.db.Exec "Delete From [UserInfo] Where UserName = '王二坛'"
	'@ *****************************************************************************************

	Public Function Exec(ByVal sql)
		On Error Resume Next
		sql = FixSQL(sql)
		If Lcase(Left(sql,6)) = "select" Then
			'Set Exec = o_conn.Execute(sql)
			'_以下等同于: Set Exec = Server.CreateObject("Adodb.Recordset") : Exec.Open sql,o_conn,1,3
			Dim i : i = i_queryType
			i_queryType = 3
			Set Exec = GRS(sql)
			i_queryType = i
		Else
			Exec = 1 : DoExecute(sql)
			If Err.number <> 0 Then Exec = 0
		End If
		If Err.number <> 0 Then
			AB.Error.Msg = "<br />" & sql
			AB.Error.Raise 25
			Err.Clear
		End If
		On Error Goto 0
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.Cmd
	'@ 语  法:  [Set rs = ]AB.db.Cmd(sql)
	'@ 返  回:  Recordset (记录集对象)
	'@ 作  用:  用ADODB.Command执行指定的SQL语句
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ rs(可选) Variable (变量) 已定义未初始化的RecordSet对象
	'@ sql String (字符串) 待执行的SQL语句
	'==DEMO=====================================================================================
	'@ 下面的例子说明了此方法的用法：
	'@ '用此方法获取记录集
	'@ Dim rs
	'@ Set rs = AB.db.Cmd("Select * From User A Right Join Dept B On A.ID=B.UserID")
	'@ '用此方法执行SQL
	'@ AB.db.Cmd "Delete From [UserInfo] Where UserName = '王二坛'"
	'@ *****************************************************************************************

	Public Function Cmd(ByVal sql)
		'Set Cmd = DoExecute(sql)
		Dim ExecuteCmd : Set ExecuteCmd = Server.CreateObject("ADODB.Command")
		sql = FixSQL(sql)
		With ExecuteCmd
			.ActiveConnection = o_conn
			.CommandText = sql
			Set Cmd = .Execute
		End With
		Set ExecuteCmd = Nothing
		Abx_DbQueryTimes = Abx_DbQueryTimes + 1
		If Err.number <> 0 Then
			AB.Error.Msg = "<br />" & sql
			AB.Error.Raise 25
			Err.Clear
		End If
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.DoSP
	'@ 语  法:  [[Set ]object = ]AB.db.DoSP(spName:spType, spParams)
	'@ 返  回:  Array (数组) 或 Numeric (数值) 或 Object (ASP对象) 或 String (字符串)
	'@ 			视存储过程类型返回不同的值，可能是数值、字符串、数组或者对象，请看示例中的详细说明
	'@ 作  用:  调用一个MSSQL存储过程并返回数据
	'@ 			调用此方法将调用一个MS SQL Server的存储过程并可返回一个或多个记录集、输出参数或返回值。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ object(可选) Variable (变量)
	'@ 返回值，可能是字符串、数组或者对象
	'@ spName String (字符串)
	'@ 要调用的存储过程名称
	'@ spType Integer (整数) 或 String (字符串)
	'@ 要调用的存储过程的类型，可以是以下值：
	'@ 0 或者 省略 - 没有参数或者只有输入参数的存储过程
	'@ 1 或者 "OUT" - 带有输出参数的存储过程
	'@ 2 或者 "RS" - 没有输出参数且返回一个或多个记录集的存储过程
	'@ 3 或者 "ALL" - 既有输出参数又返回一个或多个记录集的存储过程
	'@ spParams "" (空字符串) 或 Array (数组)
	'@ 要传递给存储过程的输入或输出参数，如是数组应遵循"数组参数约定"（稍有不同，请看示例）。
	'@ 在数组中参数的名称中请遵循以下格式：
	'@ 输入参数 : @+参数名+冒号(:)+值
	'@ 输出参数 ：@@+参数名
	'@ 注意：参数的顺序要同存储过程中的顺序完全一致
	'==DEMO=====================================================================================
	'@ 由于存储过程内容不同，返回数据的不同，输入输出参数要求的不同，所以从几种情况来看用此方法调用存储过程的过程和结果：
	'@
	'@ 1、没有输入输出参数，不返回记录集的存储过程的调用（返回“存储过程返回值”）：
	'@ Dim mySP
	'@ mySP = AB.db.DoSP("myTestSP","")
	'@ AB.C.Print mySP '输出返回值
	'@
	'@ 2、仅有输入参数，无输出参数，不返回记录集的存储过程的调用（返回“存储过程返回值”）：
	'@ mySP = AB.db.DoSP("myTestSP",Array("@param1:" & Value1, "@param2:" & Value2))
	'@ '或者用更简单的方式，只把值作为Array参数的项目即可(仅应用于这一种情况)：
	'@ mySP = AB.db.DoSP("myTestSP",Array(Value1,Value2))
	'@ AB.C.Print mySP '输出返回值
	'@
	'@ 3、有或者没有输入参数，有输出参数,不返回记录集的存储过程的调用（返回Dictionary对象）：
	'@ Set mySP = AB.db.DoSP("myTestSP:1",Array("@inParam1:" & Vlaue1,"@inParam2:" & Value2, "@@outParam1", "@@outParam2"))
	'@ AB.C.Print mySP("return") '输出返回值
	'@ AB.C.Print mySP("@@outParam1") '输出指定的输出参数值
	'@ AB.C.close(mySP)
	'@
	'@ 4、有或者没有输入参数，无输出参数，只返回记录集的存储过程的调用（返回RecordSet记录集对象）：
	'@ Set mySP = AB.db.DoSP("myTestSP:2",Array("@param1:" & Value1, "@param2:" & Value2))
	'@ While Not mySP.Eof '输出记录集内容
	'@     AB.C.Print "ID：" & mySP(0)
	'@     AB.C.Print "Name：" & mySP(1)
	'@     mySP.moveNext()
	'@ Wend
	'@ AB.db.C(mySP)
	'@
	'@ 5、有或者没有输入参数，有输出参数，也要返回记录集的存储过程的调用（返回Array数组）：
	'@ mySP = AB.db.DoSP("myTestSP:3",Array("@inParam1:" & Vlaue1,"@inParam2:" & Value2, "@@outParam1", "@@outParam2"))
	'@ 'mySP返回的将是一个数组，第一个元素是记录集对象，第二个元素是参数和返回值
	'@ Dim rs : Set rs = mySP(0)
	'@ '这里可以操作rs记录集对象了
	'@ AB.C.Print mySP(1)("return") '输出返回值
	'@ AB.C.Print mySP(1)("@@outParam2") '输出指定参数值
	'@
	'@ 再次提醒注意：参数的顺序要同存储过程中的顺序完全一致。但参数名没有强制要求要和存储过程中一样，
	'@ 只是为了代码便于阅读，建议你这么做(输出参数除外，因为这里需要两个@，存储过程中只需要一个@)。
	'@ 另外，如果你要返回多个记录集对象，可以用这种方式来对获取的多个记录集进行操作：
	'@
	'@ Set rs = AB.db.doSP("myTestSP:rs",Array("@param1:" & Value1, "@param2:" & Value2))
	'@ While Not rs.Eof '输出记录集内容
	'@     AB.C.Print "ID：" & rs(0)
	'@     AB.C.Print "Name：" & rs(1)
	'@     AB.C.Print "-----"
	'@     rs.moveNext()
	'@ Wend
	'@ AB.C.Print "==== Next Recordset ===="
	'@ Set rs = rs.NextRecordset() '获取下一个记录集
	'@ While Not rs.Eof
	'@     AB.C.Print "ProductID：" & rs(0)
	'@     AB.C.Print "Photo：" & rs(1)
	'@     AB.C.Print "-----"
	'@     rs.moveNext()
	'@ Wend
	'@ AB.db.C(rs)
	'@ *****************************************************************************************

	Public Function doSP(ByVal spName, ByVal spParam)
		On Error Resume Next
		Dim p, spType, cmd, outParam, i, NewRS : spType = ""
		If Not s_dbType="0" And Not s_dbType="MSSQL" And Not s_dbType="SQL" Then
			AB.Error.Raise 32
			Exit Function
		End If
		p = Abx_Param(spName)
		If AB.C.Has(p(1)) Then : spType = UCase(Trim(p(1))) : spName = Trim(p(0)) : p = "" : End If
		spName = FixSQL(spName)
		Set cmd = Server.CreateObject("ADODB.Command")
		With cmd
			.ActiveConnection = o_conn
			.CommandText = spName
			.CommandType = 4
			.Prepared = true
			.Parameters.append .CreateParameter("return",3,4)
			outParam = "return"
			If Not IsArray(spParam) Then
				If spParam<>"" Then
					spParam = AB.C.IIF(Instr(spParam,",")>0, spParam = Split(spParam,","), Array(spParam))
				End If
			End If
			If IsArray(spParam) Then
				For i = 0 To Ubound(spParam)
					Dim pName, pValue
					If (spType = "1" or spType = "OUT" or spType = "3" or spType = "ALL") And Instr(spParam(i),"@@")=1 Then
						.Parameters.append .CreateParameter(spParam(i),200,2,8000)
						outParam = outParam & "," & spParam(i)
					Else
						If Instr(spParam(i),"@")=1 And Instr(spParam(i),":")>2 Then
							pName = Left(spParam(i),Instr(spParam(i),":")-1)
							outParam = outParam & "," & pName
							pValue = Mid(spParam(i),Instr(spParam(i),":")+1)
							If pValue = "" Then pValue = NULL
							.Parameters.append .CreateParameter(pName,200,1,8000,pValue)
						Else
							.Parameters.append .CreateParameter("@param"&(i+1),200,1,8000,spParam(i))
							outParam = outParam & "," & "@param"&(i+1)
						End If
					End If
				Next
			End If
		End With
		outParam = AB.C.IIF(Instr(outParam,",")>0, Split(outParam,","), Array(outParam))
		If spType = "1" or spType = "OUT" Then
			cmd.Execute : doSP = cmd
		ElseIf spType = "2" or spType = "RS" Then
			Set doSP = cmd.Execute
		ElseIf spType = "3" or spType = "ALL" Then
			Dim NewOut,pa : Set NewOut = Server.CreateObject(AB.dictName)
			Set NewRS = cmd.Execute : NewRS.close
			For i = 0 To Ubound(outParam)
				NewOut(Trim(outParam(i))) = cmd(i)
			Next
			NewRs.open : doSP = Array(NewRS,NewOut)
			Set NewOut = Nothing
		Else
			cmd.Execute : doSP = cmd(0)
		End If
		Abx_DbQueryTimes = Abx_DbQueryTimes + 1
		Set cmd = Nothing
		If Err.number <> 0 Then AB.Error.Raise 24
		On Error Goto 0
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.GetPageRecord
	'@ 语  法:  Set rs = AB.db.GetPageRecord(type[:pageParam][:pageSize], arrayConfig|sql|recordset)
	'@ 别  名:  Set rs = AB.db.GPR(type[:pageParam][:pageSize], arrayConfig|sql|recordset)
	'@ 返  回:  Recordset (记录集对象) 经过分页的记录集
	'@ 作  用:  初始化分页数据并得到记录集
	'@ 			调用此方法将按指定的方式生成分页数据记录集，并设定分页导航所需的总记录数和总页数。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ rs Recordset (记录集对象)
	'@ 已定义的未初始化的记录集对象
	'@ type "" (空字符串) 或 Integer (整数) 或 String (字符串)
	'@ 获取分页数据的方式，可以是以下值：
	'@ 0 或 "array" - 用数组设置数据库查询条件生成分页。
	'@ 1 或 "sql" - 直接用SQL语句生成分页。
	'@ 2 或 "rs" - 直接用已经存在的记录集生成分页。
	'@ MSSQL存储过程名称 - 用以该字符串命名的存储过程生成分页。
	'@ ""(空字符串) - 使用默认的存储过程或者AB.db.PageSpName属性中指定的存储过程分页
	'@ pageParam(可选) String (字符串)
	'@ 获取分页页码的URL参数名称，如果省略则采用 AB.db.PageParam 属性设定的值，不设置则默认为“page”。
	'@ pageSize(可选) Integer (整数)
	'@ 每页显示的记录数，如果省略则采用 AB.db.PageSize 属性设定的值，不设置则默认为每页显示20条。
	'@ arrayConfig Array (数组)
	'@ 数组条件，仅在 type 参数的值为 0 或 "array" 或用存储过程生成分页时生效。当 type 参数不同时，此数组参数参数格式亦有所不同：
	'@
	'@    (1) type 为 0 或 "array" 时，arrayConfig 为以下格式(和 AB.db.GetRecord 类似，多一个主键参数)：
	'@    Array(Table:Fields, Where, OrderBy, PrimeKey)
	'@    Table - 表名， Fields - 字段名， Where - 条件(不包含"Where"，可以是数组，格式参照"参数约定")， OrderBy - 排序(不包含"Order By")， PrimeKey - 主键名(Access 和 MySQL 可省略此项)
	'@
	'@    (2) type 为存储过程名时，arrayConfig 格式和 AB.db.DoSP 方法的第二个参数一致，但要注意一点，必须包含两个输出参数："@@RecordCount"(总记录数)和"@@PageCount"(总页数)。
	'@
	'@    (3) 当使用AspBox自带的默认存储过程"abx_sp_pager"时，arrayConfig 为以下格式：
	'@     Array(Table, Fields, Where, OrderBy, PrimeKey, SortType)
	'@     Table - 表名， Fields - 字段名， Where - 条件(不包含"Where")， OrderBy - 排序(不包含"Order By"，SortType为3时生效，必须在最后加上主键排序，否则可能出错)， PrimeKey - 主键名, SortType - 排序类型(1主键升序，2主键降序，3其它排序)。
	'@ sql String (字符串)
	'@ 第二个参数应为用以获取记录集的SQL语句，仅在 type 参数的值为 1 或 "sql" 时生效。当使用SQL语句生成分页时，分页的方式采用原始的ADO的方式分页，所以在循环显示生成的记录集时要用rs.PageSize属性控制记录的数量，具体可见下面的示例。
	'@ recordset Recordset (记录集对象)
	'@ 一个已经存在的记录集(Recordset)对象，仅在 type 参数的值为 2 或 "rs" 时生效。
	'@ 当使用已存在记录集生成分页时，分页的方式采用原始的ADO的方式分页，所以在循环显示生成的记录集时要用rs.PageSize属性控制记录的数量，具体可见下面的示例。
	'==DEMO=====================================================================================
	'@ 下面的例子说明了采用Array方式生成分页数据方法：
	'@ Set rs = AB.db.GetPageRecord(0,Array("UserInfo:UserId,UserName","UserSex='Male' And UserAge>20","UserName Asc","UserId"))
	'@
	'@ 采用默认存储过程生成分页数据的方法：
	'@ Set rs = AB.db.GPR("abx_sp_pager",Array("UserInfo","UserId,UserName","UserSex='Male' And UserAge>20","UserName Asc,UserId Asc","UserId",3))
	'@
	'@ 采用自定义存储过程生成分页数据的方法，假设存储过程名为"myPager"：
	'@ Set rs = AB.db.GPR("myPager",Array("@Table:UserInfo","@Fields:UserId,UserName","@Condition:UserSex='Male' And UserAge>20","@Sort:UserName Asc,UserId Asc","@PrimeKey:UserId","@@RecordCount","@@PageCount"))
	'@
	'@ 如果你事先指定了 AB.db.PageSpName 属性的值，则上面的第一个参数存储过程的名称可以为空。以上代码采用了默认的页码URL参数为"page"，每页记录数为20条，如果要临时改变这些设置，可以这样：
	'@ 'url参数名为"p"，每页记录数为30条
	'@ Set rs = AB.db.GPR("0:p:30", Array("UserInfo","UserId>100","UserId Desc","UserId"))
	'@ '仅修改记录数为每页30条
	'@ Set rs = AB.db.GPR("abx_sp_pager:30", Array("UserInfo","*","UserId>100","UserId Desc","UserId",2))
	'@
	'@ 在用上面的几种方法生成分页记录集时，得到的记录集大小和设定的每页记录数大小一致。所以可以用下面的方法循环显示记录集数据：
	'@ Set rs = AB.db.GPR("0:30", Array("UserInfo","UserId>100","UserId Desc","UserId"))
	'@ While Not rs.Eof
	'@     AB.C.Print rs("UserName") & "的年龄是：" & rs("UserAge") & "岁。"
	'@     rs.MoveNext()
	'@ Wend
	'@
	'@ 还有另外两种方法，下面的例子说明了根据SQL语句生成分页数据方法：
	'@ Set rs = AB.db.GPR("sql","Select UserName, UserAge From UserInfo Where UserSex='Male' Order By UserName ASC")
	'@
	'@ 下面的例子说明了根据已存在记录集对象生成分页数据方法：
	'@ Set rsUser = AB.db.GRS("UserInfo:UserName,UserAge",Array("UserSex:Male","IsActive:1"),"UserName ASC")
	'@ Set rs = AB.db.GPR("rs:30",rsUser)
	'@
	'@ 上面这两种方法("sql"和"rs")和Access数据库在使用第一种方法("array")时都是用ADO原始的方法处理分页，
	'@ 所以如果要分页循环显示记录集数据，需要结合记录集的PageSize属性或者将记录集转为二维数组，如：
	'@ Dim rs, i
	'@ '获取记录集
	'@ Set rs = AB.db.GPR("sql","Select UserName, UserAge From UserInfo Where UserSex='Male' Order By UserName ASC")
	'@ '=========================
	'@ '循环方式一：用 While...Wend
	'@ i = 0
	'@ While Not rs.Eof And ( i < rs.PageSize )
	'@     AB.C.Print rs("UserName") & "的年龄是：" & rs("UserAge") & "岁。"
	'@     i = i + 1
	'@     rs.MoveNext()
	'@ Wend
	'@ '=========================
	'@ rs.MoveFirst()
	'@ '=========================
	'@ '循环方式二：用 For...Next
	'@ For i = 1 To rs.PageSize
	'@     If rs.Eof Then Exit For
	'@     AB.C.Print rs(0) & "的年龄是：" & rs(1) & "岁。"
	'@     rs.MoveNext()
	'@ Next
	'@ '=========================
	'@ rs.MoveFirst()
	'@ '=========================
	'@ '循环方式三：用 rs.GetRows() 和 For...Next
	'@ Dim arrRS
	'@ arrRS = rs.GetRows(rs.PageSize)
	'@ '转为数组后可以关闭记录集
	'@ AB.db.C(rs)
	'@ For i = 0 To Ubound(arrRS,2)
	'@     AB.C.Print arrRS(0,i) & "的年龄是：" & arrRS(1,i) & "岁。"
	'@ Next
	'@ '=========================
	'@ 获取更多详细分页功能示例，请查看 AspBox完整分页代码示例。
	'@ *****************************************************************************************

	Public Function GetPageRecord(ByVal PageSetup, ByVal Condition)
		On Error Resume Next
		Dim pType,spResult,rs,o,p,Sql,n,i,spReturn
		o = Abx_Param(Cstr(PageSetup))
		pType = o(0)
		If AB.C.Has(o(1)) Then
			p = Abx_Param(o(1))
			If AB.C.Has(p(1)) Then
				s_pageParam = Lcase(p(0))
				i_pageSize = Int(p(1))
			Else
				If isNumeric(o(1)) Then
					i_pageSize = Int(o(1))
				Else
					s_pageParam = Lcase(o(1))
				End If
			End If
		End If
		i_pageIndex = GetCurrentPage()
		Select Case Lcase(pType)
			Case "array","0"
				If isArray(Condition) Then
					Dim Table,Fi,Where
					o = Abx_Param(Condition(0))
					If AB.C.Has(o(1)) Then
						Table = o(0) : Fi = o(1)
					Else
						Table = Condition(0) : Fi = "*"
					End If
					If isArray(Condition(1)) Then
						Where = ValueToSql(Table,Condition(1),1)
					Else
						Where = Condition(1)
					End If
					i_recordCount = Int(RT(Table, AB.C.IIF(AB.C.isNul(Where),"1=1",Where), "Count(0)"))
					n = i_recordCount / i_pageSize
					i_pageCount = AB.C.IIF(n=Int(n), n, Int(n)+1)
					i_pageIndex = AB.C.IIF(i_pageIndex > i_pageCount, i_pageCount, i_pageIndex)
					s_dbType = UCase(s_dbType)
					If s_dbType = "1" or s_dbType = "ACCESS" or s_dbType = "ACC" or s_dbType = "" Then
						Set rs = GR(Table&":"&Fi,Where,Condition(2))
						rs.PageSize = i_pageSize
						If i_recordCount>0 Then rs.AbsolutePage = i_pageIndex
						Set GetPageRecord = rs : Exit Function
					ElseIf s_dbType = "2" or s_dbType = "MYSQL" or s_dbType = "SQL" Then
						Sql = "Select "& fi & " From [{prefix}" & Table & "]"
						If AB.C.Has(Where) Then Sql = Sql & " Where " & Where
						If AB.C.Has(Condition(2)) Then Sql = Sql & " Order By " & Condition(2)
						Sql = Sql & " Limit " & i_pageSize*(i_pageIndex-1) & ", " & i_pageSize
					Else
						If Ubound(Condition)<>3 Then AB.Error.Raise 27
						Sql = "Select Top " & i_pageSize & " " & fi
						Sql = Sql & " From [{prefix}" & Table & "]"
						If AB.C.Has(Where) Then Sql = Sql & " Where " & Where
						If i_pageIndex > 1 Then
							Sql = Sql & " " & AB.C.IIF(AB.C.isNul(Where), "Where", "And") & " " & Condition(3) & " Not In ("
							Sql = Sql & "Select Top " & i_pageSize * (i_pageIndex-1) & " " & Condition(3) & " From [{prefix}" & Table & "]"
							If AB.C.Has(Where) Then Sql = Sql & " Where " & Where
							If AB.C.Has(Condition(2)) Then Sql = Sql & " Order By " & Condition(2)
							Sql = Sql & ") "
						End If
						If AB.C.Has(Condition(2)) Then Sql = Sql & " Order By " & Condition(2)
					End If
					Set GetPageRecord = GRS(Sql)
				Else
					AB.Error.Raise 28
				End If
			Case "sql","1" Set rs = GRS(Condition)
			Case "rs","2" Set rs = Condition
			Case Else
				If isArray(Condition) Then
					If pType = "" Then pType = s_pageSpName
					Select Case pType
						Case "abx_sp_pager"
							If Ubound(Condition)<>5 Then AB.Error.Raise 29
							spResult = doSP("abx_sp_pager:3",Array("@TableName:"&Condition(0),"@FieldList:"&Condition(1),"@Where:"&Condition(2),"@Order:"&Condition(3),"@PrimaryKey:"&Condition(4),"@SortType:"&Condition(5),"@RecorderCount:0","@pageSize:"&i_pageSize,"@PageIndex:"&i_pageIndex,"@@RecordCount","@@PageCount"))
						Case Else
							spReturn = Array(False,False)
							For i = 0 To Ubound(Condition)
								If LCase(Condition(i)) = "@@recordcount" Then spReturn(0) = True
								If LCase(Condition(i)) = "@@pagecount" Then spReturn(1) = True
								If spReturn(0) And spReturn(1) Then Exit For
							Next
							If spReturn(0) And spReturn(1) Then
								spResult = doSP(pType&":3",Condition)
							Else
								AB.Error.Raise 30
							End If
					End Select
					Set GetPageRecord = spResult(0)
					i_recordCount = int(spResult(1)("@@RecordCount"))
					i_pageCount = int(spResult(1)("@@PageCount"))
					i_pageIndex = AB.C.IIF(i_pageIndex > i_pageCount, i_pageCount, i_pageIndex)
				Else
					AB.Error.Raise 31
				End If
		End Select
		If Instr(",sql,rs,1,2,", "," & pType & ",")>0 Then
			i_recordCount = rs.RecordCount
			rs.PageSize = i_pageSize
			i_pageCount = rs.PageCount
			i_pageIndex = AB.C.IIF(i_pageIndex > i_pageCount, i_pageCount, i_pageIndex)
			If i_recordCount>0 Then rs.AbsolutePage = i_pageIndex
			Set GetPageRecord = rs
		End If
		On Error Goto 0
	End Function

	Public Function GPR(ByVal PageSetup, ByVal Condition)
		Set GPR = GetPageRecord(PageSetup, Condition)
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.Pager
	'@ 语  法:  AB.db.Pager html, config
	'@ 返  回:  String (字符串) 分页链接HTML字符串
	'@ 作  用:  即时生成一个分页导航列表
	'@ 			调用此方法可以根据参数的配置即时生成和显示一个分页导航列表样式。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ html String (字符串)
	'@ 分页导航样式HTML模板(均为可选)，可以用以下代码嵌入HTML代码中代表相应的项目：
	'@   "{recordcount}" - 总记录数
	'@   "{pagecount}" - 总页数
	'@   "{pageindex}" - 当前页码
	'@   "{pagesize}" - 每页记录数
	'@   "{list}" - 分页链接列表
	'@   "{liststart}" - 分页链接列表页首导航
	'@   "{listend}" - 分页链接列表页尾导航
	'@   "{first}" - 第一页的链接
	'@   "{last}" - 最后页的链接
	'@   "{prev}" - 上一页的链接
	'@   "{next}" - 下一页的链接
	'@   "{jump}" - 页面跳转文本框或下拉菜单
	'@ config "" (空字符串) 或 Array (数组)
	'@ 分页导航样式配置选项(均为可选)。如果留空将采用默认配置，否则使用数组配置，可配置项目包括：
	'@   "recordcount" - 总记录数，通常用于测试分页效果
	'@   "pagecount" - 总页数，通常用于测试分页效果
	'@   "pageindex" - 当前页码，通常用于测试分页效果
	'@   "pagesize" - 每页记录数，通常用于测试分页效果
	'@   "listlong" - 分页链接列表页码数量，默认为9
	'@   "listsidelong" - 分页链接列表首尾导航页码数量，默认为2，html 参数中有"{liststart}"或"{listend}"时才有效
	'@   "list" - 分页链接的HTML代码，用*代表页码，默认为仅显示页码
	'@   "currentclass" - 当前页码的CSS样式名称，默认为"current"
	'@   "link" - 自定义页码链接，用*代表页码，用于静态页面分页或Ajax分页
	'@   "first" - 第一页链接的HTML代码，默认为 "&laquo;"，即显示为 ?
	'@   "prev" - 上一页链接的HTML代码，默认为"&#8249;",即显示为 ?
	'@   "next" - 下一页链接的HTML代码，默认为"&#8250;",即显示为 ?
	'@   "last" - 最后页链接的HTML代码，默认为"&raquo;",即显示为 ?
	'@   "more" - 被省略的页码链接显示为，默认为"..."
	'@   "disabledclass" - 当处于首尾页时不可用链接的CSS样式名称，默认为"disabled"
	'@   "jump" - 页面跳转方式，默认为"input"文本框，可设置为"select"下拉菜单
	'@   "jumpplus" - 页面跳转文本框或下拉菜单的附加内部代码
	'@   "jumpaction" - 跳转时要执行的javascript代码，用*代表页码，可用于Ajax分页
	'@   "jumplong" - 当跳转方式为下拉菜单时最多同时显示的页码数量，0为全部显示，默认为50
	'==DEMO=====================================================================================
	'@ 下面的例子说明了用此方可以在生成分页的时候对一些无法用 AB.db.SetPager 设定为固定值的元素，例如在一个页面上同时使用多个Ajax分页的情况：
	'@
	'@ '在同一个页面上根据记录集生成多个分页导航，同时对主记录集也进行分页(嵌套分页):
	'@ '先定义一个父类分页导航样式
	'@ AB.db.SetPager "classList", "{first}{prev}{liststart}{list}{listend}{next}{last}", Array("listlong:6","listsidelong:3","first:首页","prev:上页","next:下页","last:末页")
	'@
	'@ Dim rsParent, rsSon, pagerParent
	'@ '父类分页，每页5条记录，分页方式为array数组
	'@ Set rsParent = AB.db.GPR("array:5",Array("ClassTable","ID,className","parentId=0","sortId Asc","Id"))
	'@ pagerParent = AB.db.GetPager("classList") '生成父类分页导航并将数据预存
	'@ If Not rsParent.eof Then '如果父类有记录
	'@   While Not rsParent.eof '循环父类本页记录
	'@     AB.C.Print "" & rsParent(1) & "" '输出父类内容
	'@     '子类分页，每页10条记录，分页方式为array，页码Url参数为p
	'@     Set rsSon = AB.db.GPR("array:p:10",Array("ClassTable","ID,className","parentId="&rsParent(0),"sortId Asc","Id"))
	'@     If Not rsSon.eof Then '如果子类有记录
	'@       While Not rsSon.eof '循环子类本页记录
	'@         AB.C.Print "" & rsSon(1) & "" '输出子类内容
	'@         rsSon.movenext()
	'@       Wend
	'@       '生成子类分页导航，通过ajax方式(js函数 ajaxGo(子类ID,页码) )获取分页数据
	'@       AB.C.Print AB.db.Pager("第{liststart}{list}{listend}页,到{jump}页", Array("link:javascript:ajaxGo(" & rsSon(0) & ",*)")
	'@     End If
	'@     AB.db.C(rsSon)
	'@     rsParent.movenext()
	'@   Wend
	'@   AB.C.Print pagerParent '显示父类分页导航
	'@ End If
	'@ AB.db.C(rsParent)
	'@ *****************************************************************************************

	Public Function Pager(ByVal PagerHtml, ByRef PagerConfig)
		On Error Resume Next
		Dim pList, pListStart, pListEnd, pFirst, pPrev, pNext, pLast
		Dim pJump, pJumpLong, pJumpStart, pJumpEnd, pJumpValue
		Dim i, j, tmpStr, pStart, pEnd, cfg, pcfg(1)
		tmpStr = AB.C.IIF(PagerHtml="",o_pageDic("default_html"),PagerHtml)
		Set cfg = Server.CreateObject(AB.dictName)
		cfg("recordcount")	= i_recordCount
		cfg("pageindex")	= i_pageIndex
		cfg("pagecount")	= i_pageCount
		cfg("pagesize")		= i_pageSize
		cfg("listlong")		= 9
		cfg("listsidelong")	= 2
		cfg("list")			= "*"
		cfg("currentclass")	= "current"
		cfg("link")			= GetRQ("*")
		cfg("first")		= "&laquo;"
		cfg("prev")			= "&#8249;"
		cfg("next")			= "&#8250;"
		cfg("last")			= "&raquo;"
		cfg("more")			= "..."
		cfg("disabledclass")= "disabled"
		cfg("jump")			= "input"
		cfg("jumpplus")		= ""
		cfg("jumpaction")	= ""
		cfg("jumplong")		= 50
		PagerConfig = AB.C.IIF(isArray(PagerConfig),PagerConfig, AB.C.IIF(AB.C.isNul(PagerConfig),o_pageDic("default_config"),Array(PagerConfig,"pagerconfig:1")))
		If isArray(PagerConfig) Then
			Dim ConfigName, ConfigValue
			For i = 0 To Ubound(PagerConfig)
				ConfigName = LCase(Left(PagerConfig(i),Instr(PagerConfig(i),":")-1))
				ConfigValue = Mid(PagerConfig(i),Instr(PagerConfig(i),":")+1)
				If Instr(",recordcount,pageindex,pagecount,pagesize,listlong,listsidelong,jumplong,", ","&ConfigName&",") > 0 Then
					cfg(ConfigName) = Int(ConfigValue)
				Else
					cfg(ConfigName) = ConfigValue
				End If
			Next
		End If
		pStart = cfg("pageindex") - ((cfg("listlong") \ 2) + (cfg("listlong") Mod 2)) + 1
		pEnd = cfg("pageindex") + (cfg("listlong") \ 2)
		If pStart < 1 Then
			pStart = 1 : pEnd = cfg("listlong")
		End If
		If pEnd > cfg("pagecount") Then
			pStart = cfg("pagecount") - cfg("listlong") + 1 : pEnd = cfg("pagecount")
		End If
		If pStart < 1 Then pStart = 1
		For i = pStart To pEnd
			If i = cfg("pageindex") Then
				pList = pList & " <span class="""&cfg("currentclass")&""">" & Replace(cfg("list"),"*",i) & "</span> "
			Else
				pList = pList & " <a href="""&Replace(cfg("link"),"*",i)&""">" & Replace(cfg("list"),"*",i) & "</a> "
			End If
		Next
		If cfg("listsidelong")>0 Then
			If cfg("listsidelong") < pStart Then
				For i = 1 To cfg("listsidelong")
					pListStart = pListStart & " <a href="""&Replace(cfg("link"),"*",i)&""">" & Replace(cfg("list"),"*",i) & "</a> "
				Next
				pListStart = pListStart & AB.C.IIF(cfg("listsidelong")+1=pStart,"",cfg("more") & " ")
			ElseIf cfg("listsidelong") >= pStart And pStart > 1 Then
				For i = 1 To (pStart - 1)
					pListStart = pListStart & " <a href="""&Replace(cfg("link"),"*",i)&""">" & Replace(cfg("list"),"*",i) & "</a> "
				Next
			End If
			If (cfg("pagecount") - cfg("listsidelong")) > pEnd Then
				pListEnd = " " & cfg("more") & pListEnd
				For i = ((cfg("pagecount") - cfg("listsidelong"))+1) To cfg("pagecount")
					pListEnd = pListEnd & " <a href="""&Replace(cfg("link"),"*",i)&""">" & Replace(cfg("list"),"*",i) & "</a> "
				Next
			ElseIf (cfg("pagecount") - cfg("listsidelong")) <= pEnd And pEnd < cfg("pagecount") Then
				For i = (pEnd+1) To cfg("pagecount")
					pListEnd = pListEnd & " <a href="""&Replace(cfg("link"),"*",i)&""">" & Replace(cfg("list"),"*",i) & "</a> "
				Next
			End If
		End If
		If cfg("pageindex") > 1 Then
			pFirst = " <a href="""&Replace(cfg("link"),"*","1")&""">" & cfg("first") & "</a> "
			pPrev = " <a href="""&Replace(cfg("link"),"*",cfg("pageindex")-1)&""">" & cfg("prev") & "</a> "
		Else
			pFirst = " <span class="""&cfg("disabledclass")&""">" & cfg("first") & "</span> "
			pPrev = " <span class="""&cfg("disabledclass")&""">" & cfg("prev") & "</span> "
		End If
		If cfg("pageindex") < cfg("pagecount") Then
			pLast = " <a href="""&Replace(cfg("link"),"*",cfg("pagecount"))&""">" & cfg("last") & "</a> "
			pNext = " <a href="""&Replace(cfg("link"),"*",cfg("pageindex")+1)&""">" & cfg("next") & "</a> "
		Else
			pLast = " <span class="""&cfg("disabledclass")&""">" & cfg("last") & "</span> "
			pNext = " <span class="""&cfg("disabledclass")&""">" & cfg("next") & "</span> "
		End If
		Select Case LCase(cfg("jump"))
			Case "input"
				pJumpValue = "this.value"
				pJump = "<input type=""text"" size=""3"" title=""请输入要跳转到的页数并回车""" & AB.C.IIF(cfg("jumpplus")="",""," "&cfg("jumpplus"))
				pJump = pJump & " onkeydown=""javascript:if(event.charCode==13||event.keyCode==13){if(!isNaN(" & pJumpValue & ")){"
				pJump = pJump & AB.C.IIF(cfg("jumpaction")="",AB.C.IIF(Lcase(Left(cfg("link"),11))="javascript:",Replace(Mid(cfg("link"),12),"*",pJumpValue),"document.location.href='" & Replace(cfg("link"),"*","'+" & pJumpValue & "+'") & "';"),Replace(cfg("jumpaction"),"*", pJumpValue))
				pJump = pJump & "}return false;}"" />"
			Case "select"
				pJumpValue = "this.options[this.selectedIndex].value"
				pJump = "<select" & AB.C.IIF(cfg("jumpplus")="",""," "&cfg("jumpplus")) & " onchange=""javascript:"
				pJump = pJump & AB.C.IIF(cfg("jumpaction")="",AB.C.IIF(Lcase(Left(cfg("link"),11))="javascript:",Replace(Mid(cfg("link"),12),"*",pJumpValue),"document.location.href='" & Replace(cfg("link"),"*","'+" & pJumpValue & "+'") & "';"),Replace(cfg("jumpaction"),"*",pJumpValue))
				pJump = pJump & """ title=""请选择要跳转到的页数""> "
				If cfg("jumplong")=0 Then
					For i = 1 To cfg("pagecount")
						pJump = pJump & "<option value=""" & i & """" & AB.C.IIF(i=cfg("pageindex")," selected=""selected""","") & ">" & i & "</option> "
					Next
				Else
					pJumpLong = Int(cfg("jumplong") / 2)
					pJumpStart = AB.C.IIF(cfg("pageindex")-pJumpLong<1, 1, cfg("pageindex")-pJumpLong)
					pJumpStart = AB.C.IIF(cfg("pagecount")-cfg("pageindex")<pJumpLong, pJumpStart-(pJumpLong-(cfg("pagecount")-cfg("pageindex")))+1, pJumpStart)
					pJumpStart = AB.C.IIF(pJumpStart<1,1,pJumpStart)
					j = 1
					For i = pJumpStart To cfg("pageindex")
						pJump = pJump & "<option value=""" & i & """" & AB.C.IIF(i=cfg("pageindex")," selected=""selected""","") & ">" & i & "</option> "
						j = j + 1
					Next
					pJumpLong = AB.C.IIF(cfg("pagecount")-cfg("pageindex")<pJumpLong, pJumpLong, pJumpLong + (pJumpLong-j)+1)
					pJumpEnd = AB.C.IIF(cfg("pageindex")+pJumpLong>cfg("pagecount"), cfg("pagecount"), cfg("pageindex")+pJumpLong)
					For i = cfg("pageindex")+1 To pJumpEnd
						pJump = pJump & "<option value=""" & i & """>" & i & "</option> "
					Next
				End If
				pJump = pJump & "</select>"
		End Select
		tmpStr = Replace(tmpStr,"{recordcount}",cfg("recordcount"))
		tmpStr = Replace(tmpStr,"{pagecount}",cfg("pagecount"))
		tmpStr = Replace(tmpStr,"{pageindex}",cfg("pageindex"))
		tmpStr = Replace(tmpStr,"{pagesize}",cfg("pagesize"))
		tmpStr = Replace(tmpStr,"{list}",pList)
		tmpStr = Replace(tmpStr,"{liststart}",pListStart)
		tmpStr = Replace(tmpStr,"{listend}",pListEnd)
		tmpStr = Replace(tmpStr,"{first}",pFirst)
		tmpStr = Replace(tmpStr,"{prev}",pPrev)
		tmpStr = Replace(tmpStr,"{next}",pNext)
		tmpStr = Replace(tmpStr,"{last}",pLast)
		tmpStr = Replace(tmpStr,"{jump}",pJump)
		Set cfg = Nothing
		Pager = vbCrLf & tmpStr & vbCrLf
		On Error Goto 0
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.SetPager
	'@ 语  法:  AB.db.SetPager name, html, config
	'@ 返  回:  无返回值
	'@ 作  用:  设置分页导航列表样式
	'@ 			调用此方法可以设置一个或多个分页导航列表样式作为预先配置，在显示分页时直接用 AB.db.GetPager 方法调用这些设置。
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ name String (字符串)
	'@ 分页导航样式配置名称，默认样式的名称为"default"
	'@ html String (字符串)
	'@ 分页导航样式HTML模板，可以用以下代码嵌入HTML代码中代表相应的项目(均为可选)：
	'@   "{recordcount}" - 总记录数
	'@   "{pagecount}" - 总页数
	'@   "{pageindex}" - 当前页码
	'@   "{pagesize}" - 每页记录数
	'@   "{list}" - 分页链接列表
	'@   "{liststart}" - 分页链接列表页首导航
	'@   "{listend}" - 分页链接列表页尾导航
	'@   "{first}" - 第一页的链接
	'@   "{last}" - 最后页的链接
	'@   "{prev}" - 上一页的链接
	'@   "{next}" - 下一页的链接
	'@   "{jump}" - 页面跳转文本框或下拉菜单
	'@ config "" (空字符串) 或 Array (数组)
	'@ 分页导航样式配置选项。如果留空将采用默认配置，否则使用数组配置，可配置项目包括：
	'@   "listlong" - 分页链接列表页码数量，默认为9
	'@   "listsidelong" - 分页链接列表首尾导航页码数量，默认为2，html 参数中有"{liststart}"或"{listend}"时才有效
	'@   "list" - 分页链接的HTML代码，用*代表页码，默认为仅显示页码
	'@   "currentclass" - 当前页码的CSS样式名称，默认为"current"
	'@   "link" - 自定义页码链接，用*代表页码，用于静态页面分页或Ajax分页
	'@   "first" - 第一页链接的HTML代码，默认为 "&laquo;"，即显示为 ?
	'@   "prev" - 上一页链接的HTML代码，默认为"&#8249;",即显示为 ?
	'@   "next" - 下一页链接的HTML代码，默认为"&#8250;",即显示为 ?
	'@   "last" - 最后页链接的HTML代码，默认为"&raquo;",即显示为 ?
	'@   "more" - 被省略的页码链接显示为，默认为"..."
	'@   "disabledclass" - 当处于首尾页时不可用链接的CSS样式名称，默认为"disabled"
	'@   "jump" - 页面跳转方式，默认为"input"文本框，可设置为"select"下拉菜单
	'@   "jumpplus" - 页面跳转文本框或下拉菜单的附加内部代码
	'@   "jumpaction" - 跳转时要执行的javascript代码，用*代表页码，可用于Ajax分页
	'@   "jumplong" - 当跳转方式为下拉菜单时最多同时显示的页码数量，0为全部显示，默认为50
	'==DEMO=====================================================================================
	'@ 下面的例子说明了此方法的用法：
	'@ '更改默认分页导航样式
	'@ AB.db.SetPager "default", "<div class=""newpager"">{first}{prev}{list}{next}{last} 转到{jump}页</div>", Array("listlong:10","first:首页","last:尾页","prev:上一页","next:下一页","list:第*页")
	'@ '新增一个分页导航样式
	'@ AB.db.SetPager "base", "{first}{prev}{liststart}{list}{listend}{next}{last}", Array("listlong:6","listsidelong:3")
	'@ '新增一个链接到静态分页的导航样式
	'@ AB.db.SetPager "tohtml", "<div id=tohtml>{first}{prev}{liststart}{list}{listend}{next}{last}</div>", Array("link:/news/list-*.html")
	'@ '新增一个Ajax分页导航样式
	'@ AB.db.SetPager "ajax", "<div class=""ajax"">第{liststart}{list}{listend}页,到{jump}页</div>", Array("link:javascript:ajaxGo(*)","jumpaction:ajaxGo(*)")
	'@ '新增一个包含丰富内容的分页导航样式，并采用下拉菜单跳转
	'@ AB.db.SetPager "all", "{first}{prev}{liststart}{list}{listend}{next}{last} 共{recordcount}条，每页{pagesize}条，当前第{pageindex}页/共{pagecount}页,跳转到{jump}页", Array("jump:select","jumplong:30","jumpplus:style=""color:#666""")
	'@ *****************************************************************************************

	Public Sub SetPager(ByVal PagerName, ByVal PagerHtml, ByRef PagerConfig)
		If PagerName = "" Then PagerName = "default"
		If AB.C.Has(PagerHtml) Then o_pageDic.item(PagerName&"_html") = PagerHtml
		If AB.C.Has(PagerConfig) Then o_pageDic.item(PagerName&"_config") = PagerConfig
	End Sub

	Public Function GetPager(ByVal PagerName)
		If PagerName = "" Then PagerName = "default"
		GetPager = Pager(o_pageDic(PagerName&"_html"),o_pageDic(PagerName&"_config"))
	End Function

	'返回影响的行数，如直接执行Update或Delete语句操作。
	Public Function AffectedRows()
		If IsEmpty(i_affected) Then i_affected = 0
		AffectedRows = i_affected
	End Function

	'获取某表上次插入记录Id
	Public Function LastId(ByVal table)
		LastId = o_conn.Execute("SELECT @@IDENTITY FROM "& s_tbPrefix & table)(0)
	End Function

	'@ *****************************************************************************************
	'@ 过程名:  AB.db.FixSQL
	'@ 语  法:  AB.db.FixSQL(sql)
	'@ 返  回:  String (字符串) 修正SQL语句
	'@ 作  用:  修正SQL语句，替换SQL语句中的{prefix}、{suffix}等有特殊意义的关键字
	'==DESC=====================================================================================
	'@ 参数说明：
	'@ sql String (字符串) SQL语句
	'==DEMO=====================================================================================
	'@ ab.db.tbPrefix = "tb_"
	'@ dim sql : sql = ab.db.fixsql("select * from {prefix}test01")
	'@ ab.c.print sql '结果：上面sql语句中的“{prefix}”被替换为“tb_”
	'@ *****************************************************************************************

	Public Function FixSQL(ByVal str)
		Dim temp : temp = str
		temp = AB.C.RP(temp, "{prefix}" ,s_tbPrefix)
		temp = AB.C.RP(temp, "{suffix}" ,s_tbSuffix)
		FixSQL = temp
	End Function

	Public Function DelFix(ByVal str)
		Dim temp : temp = str
		temp = AB.C.RP(temp, "{prefix}" ,"")
		temp = AB.C.RP(temp, "{suffix}" ,"")
		DelFix = temp
	End Function

	Private Function ValueToSql(ByVal TableName, ByVal ValueList, ByVal sType)
		On Error Resume Next
		Dim StrTemp : StrTemp = ValueList
		If IsArray(ValueList) Then
			StrTemp = ""
			Dim rsTemp, CurrentField, CurrentValue, CurrentSign, i
			Set rsTemp = GRS("Select Top 1 * From [{prefix}" & TableName & "] Where 1 = -1")
			For i = 0 to Ubound(ValueList)
				CurrentField = Abx_Param(ValueList(i))(0)
				CurrentValue = Abx_Param(ValueList(i))(1)
				CurrentSign = Abx_Param(ValueList(i))(2)
				If i <> 0 Then StrTemp = StrTemp & AB.C.IIF(sType=1, " And ", ", ")
				If sType = 2 Then
					StrTemp = StrTemp & "[" & CurrentField & "]"
				Else
					If Trim(CurrentValue)<>"" Then
						s_dbType = UCase(Cstr(dbType))
						If s_dbType = "" Then s_dbType = "ACCESS"
						If CurrentSign = "" Then CurrentSign = "="
						Select Case rsTemp.Fields(CurrentField).Type
							Case 8,129,130,133,134,200,201,202,203
								StrTemp = StrTemp & AB.C.IIF(sType = 3, "'"&CurrentValue&"'", "[" & CurrentField & "] "&CurrentSign&" '"&CurrentValue&"'")
							Case 7,135
								CurrentValue = AB.C.IIF(AB.C.isNul(CurrentValue),"NULL","'"&CurrentValue&"'")
								StrTemp = StrTemp & AB.C.IIF(sType = 3, CurrentValue, "[" & CurrentField & "] "&CurrentSign&" " & CurrentValue)
							Case 11
								Dim tmpTF, tmpTFV : tmpTFV = UCase(cstr(Trim(CurrentValue)))
								tmpTF = AB.C.IIF(tmpTFV="TRUE" or tmpTFV = "1", AB.C.IIF(s_dbType="ACCESS","True","1"), AB.C.IIF(s_dbType="ACCESS",AB.C.IIF(tmpTFV="","NULL","False"),AB.C.IIF(tmpTFV="","NULL","0")))
								StrTemp = StrTemp & AB.C.IIF(sType = 3, tmpTF, "[" & CurrentField & "] "&CurrentSign&" " & tmpTF)
							Case Else
								CurrentValue = AB.C.IIF(AB.C.isNul(CurrentValue),"NULL",CurrentValue)
								StrTemp = StrTemp & AB.C.IIF(sType = 3, CurrentValue, "[" & CurrentField & "] "&CurrentSign&" " & CurrentValue)
						End Select
					Else
						StrTemp = StrTemp & CurrentField & ""
					End If
				End If
			Next
			rsTemp.Close() : Set rsTemp = Nothing
			If Err.number <> 0 Then AB.Error.Raise 26 : Err.Clear
		End If
		ValueToSql = StrTemp
		On Error Goto 0
	End Function

	Private Function DoExecute(ByVal sql)
		On Error Resume Next
		Dim ExecuteCmd
		sql = FixSQL(sql)
		i_affected = 0
		''Set DoExecute = o_conn.Execute(sql, i_affected, adCmdText)
		Set DoExecute = o_conn.Execute(sql, i_affected, 1)
		If Err.Number <> 0 Then
			Err.Clear()
			Set ExecuteCmd = Server.CreateObject("ADODB.Command")
			With ExecuteCmd
				.ActiveConnection = o_conn
				''.Commandtype = adCmdText
				.Commandtype = 1
				.CommandText = sql
				''Set DoExecute = .Execute
				Set DoExecute = .Execute(i_affected,,adExecuteNoRecords)
				If Err.Number <> 0 Then
					Err.Clear()
					Set DoExecute = .Execute
				End If
			End With
			Set ExecuteCmd = Nothing
		End If
		On Error Goto 0
	End Function

	Private Function Abx_tbExists(Byval table)
		Dim rsSchema,isExist : isExist = False
		Set rsSchema = o_conn.openSchema(20)
		Dim tb : tb = table : tb = "{prefix}" & DelFix(tb)
		tb = FixSQL(tb)
		rsSchema.MoveFirst
		Do Until rsSchema.EOF
			If rsSchema("TABLE_TYPE")="TABLE" Then
				If LCase(rsSchema("TABLE_NAME")) = LCase(tb) Then
					isExist = True
					Exit Do
				End If
			End If
			rsSchema.movenext
		Loop
		Close(rsSchema)
		Abx_tbExists = isExist
		If Err.Number <> 0 Then
			AB.Error.Msg = "<br />" & Err.Description
			AB.Error.Raise 8
			Err.Clear
		End If
	End Function

	Private Function GetCurrentPage()
		Dim rqParam, thisPage : thisPage = 1
		rqParam = AB.C.Get(s_pageParam)
		If isNumeric(rqParam) Then
			If Int(rqParam) > 0 Then thisPage = Int(rqParam)
		End If
		GetCurrentPage = thisPage
	End Function

	Private Function GetRQ(pageNumer)
		GetRQ = AB.C.ReplaceUrl(s_pageParam, pageNumer)
	End Function

	Private Function isSQLAutoID(ByVal typeid, ByVal flag)
		isSQLAutoID = False
		Dim tmp1, tmp2
		Select Case typeid
			Case 2
				tmp1 = "smallint"
				tmp2 = AB.C.IIF(flag=16,"自增","")
			Case 3
				tmp1 = "int"
				tmp2 = AB.C.IIF(flag=20,"自增","")
			Case 17
				tmp1 = "tinyint"
				tmp2 = AB.C.IIF(flag=16,"自增","")
			Case 20
				tmp1 = "bigint"
				tmp2 = AB.C.IIF(flag=16,"自增","")
		End Select
		If tmp2="自增" Then isSQLAutoID = True
	End Function

	Private Function isACCAutoID(ByVal typeid, ByVal flag)
		isACCAutoID = False
		Dim tmp1, tmp2
		Select Case typeid
			Case 2
				tmp1 = "num"
			Case 3
				tmp1 = "int"
				tmp2 = AB.C.IIF(flag=90,"自增","")
		End Select
		If tmp2="自增" Then isACCAutoID = True
	End Function

	Private Function Abx_Param(ByVal s)
		Dim arr(2),t : t = Instr(s,":")
		If t > 0 Then
			arr(0) = Trim(Left(s,t-1)) : arr(1) = Trim(Mid(s,t+1))
		Else
			If trim(s)="" Then
				arr(0) = "" : arr(1) = "" : arr(2) = ""
			Else
				If Instr(s,">=") > 0 Then
					arr(0) = Trim(AB.C.CLeft(s,">=")) : arr(1) = Trim(AB.C.CRight(s,">=")) : arr(2) = ">="
				ElseIf Instr(s,"<=") > 0 Then
					arr(0) = Trim(AB.C.CLeft(s,"<=")) : arr(1) = Trim(AB.C.CRight(s,"<=")) : arr(2) = "<="
				ElseIf Instr(s,"<>") > 0 Then
					arr(0) = Trim(AB.C.CLeft(s,"<>")) : arr(1) = Trim(AB.C.CRight(s,"<>")) : arr(2) = "<>"
				ElseIf Instr(s,"!=") > 0 Then
					arr(0) = Trim(AB.C.CLeft(s,"<=")) : arr(1) = Trim(AB.C.CRight(s,"!=")) : arr(2) = "<>"
				ElseIf Instr(s,">") > 0 Then
					arr(0) = Trim(AB.C.CLeft(s,">")) : arr(1) = Trim(AB.C.CRight(s,">")) : arr(2) = ">"
				ElseIf Instr(s,"<") > 0 Then
					arr(0) = Trim(AB.C.CLeft(s,"<")) : arr(1) = Trim(AB.C.CRight(s,"<")) : arr(2) = "<"
				ElseIf Instr(s,"=") > 0 Then
					arr(0) = Trim(AB.C.CLeft(s,"=")) : arr(1) = Trim(AB.C.CRight(s,"=")) : arr(2) = "="
				Else
					arr(0) = s : arr(1) = "" : arr(2) = ""
				End If
			End If
		End If
		Abx_Param = arr
	End Function

	Private Function getConnStr__()
		Dim a_sql(5), a_acc(2), a_mysql(1), a_oracle(1), tempstr
		If s_dbType = "" Or UCase(s_dbType) = "ACC" Then s_dbType = "ACCESS"
		If UCase(s_dbType) = "SQL" Then s_dbType = "MSSQL"
		IF UCase(s_dbType) = "SQL" Or UCase(s_dbType) = "MSSQL" Then
			a_sql(0) = "DRIVER={SQL SERVER}; Server = " & sql.server & AB.C.IfThen(AB.C.Has(sql.port), "," & sql.port) & "; Uid = " & sql.uid & "; Pwd = " & sql.pwd & "; Database = " & sql.db & ";"
			a_sql(1) = "Provider = SQLOLEDB; Server = " & sql.server & AB.C.IfThen(AB.C.Has(sql.port), "," & sql.port) & "; Uid = " & sql.uid & "; Pwd = " & sql.pwd & "; Database = " & sql.db & ";"
			a_sql(2) = "Provider = SQLOLEDB.1; Persist Security Info=False; Server=" & sql.server & AB.C.IfThen(AB.C.Has(sql.port), "," & sql.port) & ",1433; User ID = " & sql.uid & "; Password = " & sql.pwd & "; Database = " & sql.db & ";"
			a_sql(3) = "Provider = SQLOLEDB; Data Source = " & sql.server & AB.C.IfThen(AB.C.Has(sql.port), "," & sql.port) & "; Initial Catalog = " & sql.db & "; User ID = " & sql.uid & "; Password = " & sql.pwd & ";"
			a_sql(4) = "Provider = SQLOLEDB.1; DATA SOURCE=" & sql.server & AB.C.IfThen(AB.C.Has(sql.port), "," & sql.port) & "; UID="& sql.uid &"; PWD="& sql.pwd &"; Database="& sql.db &"; Pooling=true; MAX Pool Size=512; Min Pool Size=50; Connection Lifetime=30"
			tempstr = a_sql(3)
		ElseIf UCase(s_dbType) = "MYSQL" Then
			If mysql.port = "" Then mysql.port = "3306"
			a_mysql(0) = "Driver={mySQL};Server=" & mysql.server & ";Port=" & mysql.port & ";Option=131072;Stmt=;Database=" & mysql.db & ";Uid=" & mysql.uid & ";Pwd=" & mysql.pwd & ";"
			tempstr = a_mysql(0)
		ElseIf UCase(s_dbType) = "ORACLE" Then
			a_oracle(0) = "Provider=msdaora;Data Source=" & oracle.server & ";User Id=" & oracle.uid & ";Password=" & oracle.pwd & ";"
			tempstr = a_oracle(0)
		Else
			Dim tDb : tDb = acc.db : If Instr(acc.db,":")>0 Then : tDb = acc.db : Else : If Trim(tDb) <> "" Then tDb = Server.MapPath(acc.db) : End If
			a_acc(0) = "DBQ="+ tDb +";DefaultDir=;DRIVER={Microsoft Access Driver (*.mdb)};"
			a_acc(1) = "Provider=Microsoft.Jet.OLEdb.4.0;Data Source=" & tDb & ";"
			'==acc带密码
			If acc.pwd<>"" Then a_acc(0) = a_acc(0) & "Persist Security Info=False;pwd="& acc.pwd &""
			If acc.pwd<>"" Then a_acc(1) = a_acc(1) & "Persist Security info=false;Jet OLEDB:Database password="& acc.pwd &""
			tempstr = a_acc(1)
		End IF
		s_connstr = tempstr
		getConnStr__ = s_connstr
	End Function

End Class
Class Cls_AB_DB_MSSQL : Dim [server], db, uid, pwd, port: End Class
Class Cls_AB_DB_ACCESS : Dim db, pwd: End Class
Class Cls_AB_DB_MYSQL : Dim [server], uid, pwd, port, db: End Class
Class Cls_AB_DB_ORACLE : Dim [server], uid, pwd, db: End Class
%>